diff --git a/apps/design-system/styles/code-block-variables.css b/apps/design-system/styles/code-block-variables.css index d4a7fe54297..f1054071748 100644 --- a/apps/design-system/styles/code-block-variables.css +++ b/apps/design-system/styles/code-block-variables.css @@ -1,7 +1,5 @@ [data-theme='dark'], -[data-theme='deep-dark'], -.dark, -.deep-dark { +.dark { --code-token-keyword: #bda4ff; --code-foreground: #ffffff; --code-token-constant: #3ecf8e; diff --git a/apps/docs/styles/main.css b/apps/docs/styles/main.css index 48534b2397d..340a8a8a369 100644 --- a/apps/docs/styles/main.css +++ b/apps/docs/styles/main.css @@ -3,7 +3,7 @@ @tailwind utilities; @import './../../../packages/ui/build/css/source/global.css'; -@import './../../../packages/ui/build/css/themes/dark-combined.css'; +@import './../../../packages/ui/build/css/themes/dark.css'; @import './../../../packages/ui/build/css/themes/light.css'; @font-face { @@ -324,9 +324,7 @@ th code { /* Code block theme colors for use with Supabase Theme */ [data-theme='dark'], -[data-theme='deep-dark'], -.dark, -.deep-dark { +.dark { --code-token-keyword: #bda4ff; --code-foreground: #ffffff; --code-token-constant: #3ecf8e; diff --git a/apps/learn/styles/code-block-variables.css b/apps/learn/styles/code-block-variables.css index d4a7fe54297..f1054071748 100644 --- a/apps/learn/styles/code-block-variables.css +++ b/apps/learn/styles/code-block-variables.css @@ -1,7 +1,5 @@ [data-theme='dark'], -[data-theme='deep-dark'], -.dark, -.deep-dark { +.dark { --code-token-keyword: #bda4ff; --code-foreground: #ffffff; --code-token-constant: #3ecf8e; diff --git a/apps/lite-studio/app/app.css b/apps/lite-studio/app/app.css index b864d8d804f..67093bff5cd 100644 --- a/apps/lite-studio/app/app.css +++ b/apps/lite-studio/app/app.css @@ -1,5 +1,5 @@ @import './../../../packages/ui/build/css/source/global.css'; -@import './../../../packages/ui/build/css/themes/dark-combined.css'; +@import './../../../packages/ui/build/css/themes/dark.css'; @import './../../../packages/ui/build/css/themes/light.css'; @tailwind components; diff --git a/apps/studio/styles/main.css b/apps/studio/styles/main.css index d432650736e..96df8ea6110 100644 --- a/apps/studio/styles/main.css +++ b/apps/studio/styles/main.css @@ -25,8 +25,6 @@ --header-height: 3rem; } -[data-theme='deep-dark'], -.deep-dark, [data-theme='dark'], .dark { --chart-1: var(--brand-default); diff --git a/apps/ui-library/styles/code-block-variables.css b/apps/ui-library/styles/code-block-variables.css index d4a7fe54297..f1054071748 100644 --- a/apps/ui-library/styles/code-block-variables.css +++ b/apps/ui-library/styles/code-block-variables.css @@ -1,7 +1,5 @@ [data-theme='dark'], -[data-theme='deep-dark'], -.dark, -.deep-dark { +.dark { --code-token-keyword: #bda4ff; --code-foreground: #ffffff; --code-token-constant: #3ecf8e; diff --git a/apps/www/styles/index.css b/apps/www/styles/index.css index 70a98e91aca..ba88c5b430c 100644 --- a/apps/www/styles/index.css +++ b/apps/www/styles/index.css @@ -5,7 +5,7 @@ @import url(../pages/launch-week/launchWeek.css); @import './../../../packages/ui/build/css/source/global.css'; -@import './../../../packages/ui/build/css/themes/dark-combined.css'; +@import './../../../packages/ui/build/css/themes/dark.css'; @import './../../../packages/ui/build/css/themes/light.css'; @layer utilities { diff --git a/packages/common/tailwind.config.js b/packages/common/tailwind.config.js index 9c2af498997..fd65503e32a 100644 --- a/packages/common/tailwind.config.js +++ b/packages/common/tailwind.config.js @@ -1 +1 @@ -module.exports = require('config/tailwind.config') +// Placeholder so that VSCode tailwind extension enables intellisense in this project diff --git a/packages/config/tailwind-plugins/motion-safe-transition.js b/packages/config/tailwind-plugins/motion-safe-transition.js new file mode 100644 index 00000000000..7f514707acf --- /dev/null +++ b/packages/config/tailwind-plugins/motion-safe-transition.js @@ -0,0 +1,85 @@ +const plugin = require('tailwindcss/plugin') +/** + * Plugin to add `safe` versions of the `transition-*` properties, which respect + * `prefers-reduced-motion`. + * + * When users prefer reduced motion, the duration of transform transitions is + * reduced to something negiglible (1ms). The original `transition-*` properties + * aren't overridden to provide flexibility, in situations where you want to + * handle the `prefers-reduced-motion` case some other way. + * + * See https://css-tricks.com/levels-of-fix/. + * + * Usage:
+ * - Transitioned properties will animate with duration 100, _except_ + * transform properties when prefers-reduced-motion is on, which + * will animate instantaneously. + * + * Note: + * - `duration-safe` must be used with `transition-safe` + * - Non-safe `duration` must be used with non-safe `transition` + * - (Cannot be mixed) + */ +module.exports = plugin(function ({ addUtilities, matchUtilities, theme }) { + addUtilities({ + '.transition-safe': { + transitionProperty: + 'color, transform, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, filter, backdrop-filter', + transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', + transitionDuration: '150ms', + '@media (prefers-reduced-motion)': { + transitionDuration: + '150ms, 1ms, 150ms, 150ms, 150ms, 150ms, 150ms, 150ms, 150ms, 150s, 150ms', + }, + }, + '.transition-safe-all': { + transitionProperty: 'all, transform', + transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', + transitionDuration: '150ms', + '@media (prefers-reduced-motion)': { + transitionDuration: '150ms, 1ms', + }, + }, + '.transition-safe-transform': { + /** + * The duplicate `transform` here is a hacky way of dealing with the fact + * that `transform` must be second in `transition-safe-all` to override + * `all`, and its order must be the same across all `transition-safe-*` + * classes, so the proper duration applies in `duration-safe`. + */ + transitionProperty: 'transform, transform', + transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', + transitionDuration: '150ms', + '@media (prefers-reduced-motion)': { + transitionDuration: '1ms', + }, + }, + /* Hide scrollbar for Chrome, Safari and Opera */ + '.no-scrollbar::-webkit-scrollbar': { + display: 'none', + }, + /* Hide scrollbar for IE, Edge and Firefox */ + '.no-scrollbar': { + '-ms-overflow-style': 'none' /* IE and Edge */, + 'scrollbar-width': 'none' /* Firefox */, + }, + }) + + matchUtilities( + { + 'duration-safe': (value) => ({ + transitionDuration: value, + '@media (prefers-reduced-motion)': { + /** + * Preserves the indicated duration for everything except `transform`. + * + * Relies on browsers truncating the `transition-duration` property + * if there are more values than there are transitioned properties. + */ + transitionDuration: `${value}, 1ms, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}`, + }, + }), + }, + { values: theme('transitionDuration') } + ) +}) diff --git a/packages/config/tailwind.config.js b/packages/config/tailwind.config.js index 07e7dbbf447..78b8a7cea44 100644 --- a/packages/config/tailwind.config.js +++ b/packages/config/tailwind.config.js @@ -434,98 +434,13 @@ const uiConfig = ui({ require('@tailwindcss/typography'), require('tailwindcss-animate'), require('./tailwind-plugins/hit-area'), - plugin(motionSafeTransition), + require('./tailwind-plugins/motion-safe-transition'), function ({ addVariant }) { addVariant('not-disabled', '&:not(:disabled)') }, ], }) -/** - * Plugin to add `safe` versions of the `transition-*` properties, which respect - * `prefers-reduced-motion`. - * - * When users prefer reduced motion, the duration of transform transitions is - * reduced to something negiglible (1ms). The original `transition-*` properties - * aren't overridden to provide flexibility, in situations where you want to - * handle the `prefers-reduced-motion` case some other way. - * - * See https://css-tricks.com/levels-of-fix/. - * - * Usage:
- * - Transitioned properties will animate with duration 100, _except_ - * transform properties when prefers-reduced-motion is on, which - * will animate instantaneously. - * - * Note: - * - `duration-safe` must be used with `transition-safe` - * - Non-safe `duration` must be used with non-safe `transition` - * - (Cannot be mixed) - */ -function motionSafeTransition({ addUtilities, matchUtilities, theme }) { - addUtilities({ - '.transition-safe': { - transitionProperty: - 'color, transform, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, filter, backdrop-filter', - transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', - transitionDuration: '150ms', - '@media (prefers-reduced-motion)': { - transitionDuration: - '150ms, 1ms, 150ms, 150ms, 150ms, 150ms, 150ms, 150ms, 150ms, 150s, 150ms', - }, - }, - '.transition-safe-all': { - transitionProperty: 'all, transform', - transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', - transitionDuration: '150ms', - '@media (prefers-reduced-motion)': { - transitionDuration: '150ms, 1ms', - }, - }, - '.transition-safe-transform': { - /** - * The duplicate `transform` here is a hacky way of dealing with the fact - * that `transform` must be second in `transition-safe-all` to override - * `all`, and its order must be the same across all `transition-safe-*` - * classes, so the proper duration applies in `duration-safe`. - */ - transitionProperty: 'transform, transform', - transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)', - transitionDuration: '150ms', - '@media (prefers-reduced-motion)': { - transitionDuration: '1ms', - }, - }, - /* Hide scrollbar for Chrome, Safari and Opera */ - '.no-scrollbar::-webkit-scrollbar': { - display: 'none', - }, - /* Hide scrollbar for IE, Edge and Firefox */ - '.no-scrollbar': { - '-ms-overflow-style': 'none' /* IE and Edge */, - 'scrollbar-width': 'none' /* Firefox */, - }, - }) - - matchUtilities( - { - 'duration-safe': (value) => ({ - transitionDuration: value, - '@media (prefers-reduced-motion)': { - /** - * Preserves the indicated duration for everything except `transform`. - * - * Relies on browsers truncating the `transition-duration` property - * if there are more values than there are transitioned properties. - */ - transitionDuration: `${value}, 1ms, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}, ${value}`, - }, - }), - }, - { values: theme('transitionDuration') } - ) -} - function arrayMergeFn(destinationArray, sourceArray) { return destinationArray.concat(sourceArray).reduce((acc, cur) => { if (acc.includes(cur)) return acc diff --git a/packages/ui/README.md b/packages/ui/README.md index 13477143e16..cbf510fe4f5 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -1,38 +1,28 @@ -# Supabase UI Package +# `packages/ui` -## Figma-tokens setup +Supabase's shared React component library. Built on [Radix UI](https://www.radix-ui.com/) primitives and [shadcn/ui](https://ui.shadcn.com/), styled with Tailwind CSS, and used across all Supabase apps. -Tokens setup is based partly on blogpost on the [Mirahi Digital Garden](https://garden.mirahi.io/how-to-use-the-color-tokens-from-your-design-system-directly-in-tailwind-css/). +## Usage -Transforms tokens stored on [Figma Tokens](https://github.com/six7/figma-tokens) using token-transformer and [Style Dictionary](https://github.com/amzn/style-dictionary) to css-variables with references, and use them in your [TailwindCSS](https://github.com/tailwindlabs/tailwindcss) environment with multiple themes. +Import from the `'ui'` package alias: -### Build all the styles files (css-variables and tailwind config) - -```bash -npm run build-styles +```tsx +import { Badge, Button, Input } from 'ui' ``` -This creates a directory called `styles` with the tokens and also CSS required. +Some of the components have the `_Shadcn_` suffix. These components should be preferred, they're in a process of replacing the other ones. -### Other examples +### Utilities -You can find [other examples here](https://github.com/six7/figma-tokens-examples) by [Jan Six](https://twitter.com/six7) - -### Adding new themes/sets - -Add new sets by extending the scripts in package.json. Currently theme sets derive from `exported/[set name]` but can come from anywhere. - -Adding a new set can be done with the following: - -```json package.json -// package.json - -//.. -"build-transform-light": "npx token-transformer tokens.json styles/tokens/02_themes/light.json global,exported/light,theme global", -// add a new line like this: -"build-transform-new": "npx token-transformer tokens.json styles/tokens/02_themes/new.json global,new,theme global", -//.. -// append the new theme/set command to end of the build-transform command -"build-transform": "npm run build-transform-global && npm run build-transform-typography && npm run build-transform-dark && npm run build-transform-light && npm run build-transform-new", +```tsx +// deep object merge (used for themes) +import { clipboard, cn, mergeDeep } from 'ui' // clsx + tailwind-merge +// copy-to-clipboard helper ``` + +## Styling conventions + +- Tailwind only — no inline styles or CSS modules. +- Use semantic tokens (`bg-muted`, `text-foreground-light`, `border-default`) rather than hardcoded colors. +- The workspace root owns the actual `tailwind.config.js`. The file in this package is a stub for IntelliSense only. diff --git a/packages/ui/build/css/themes/concept-two.css b/packages/ui/build/css/themes/concept-two.css deleted file mode 100644 index dd768064c48..00000000000 --- a/packages/ui/build/css/themes/concept-two.css +++ /dev/null @@ -1,64 +0,0 @@ -[data-theme='concept-two'], -.concept-two { - --helpers-os-appearance: Dark; - --code-block-5: 0deg 0% 100%; - --code-block-4: 0deg 0% 100%; - --code-block-3: 0deg 0% 100%; - --code-block-2: 0deg 0% 100%; - --code-block-1: 0deg 0% 100%; - --secondary-default: 21deg 90.4% 59%; - --secondary-400: 20deg 93.2% 40.4%; - --secondary-200: 17.7deg 78.9% 27.8%; - --brand-link: 163.9deg 53.2% 15.1%; - --brand-default: 163.9deg 53.2% 15.1%; - --brand-600: 156.3deg 59.5% 70%; - --brand-500: 175.6deg 61.8% 21.6%; - --brand-400: 176deg 65.2% 13.5%; - --brand-300: 176.3deg 69.6% 9%; - --brand-200: 175deg 75% 6.3%; - --warning-default: 41.2deg 67.5% 48.2%; - --warning-600: 41deg 80.6% 42.4%; - --warning-500: 39.7deg 70.3% 19.8%; - --warning-400: 38.8deg 69.9% 14.3%; - --warning-300: 41.8deg 60% 10.8%; - --warning-200: 60deg 42.1% 7.5%; - --destructive-default: 10.2deg 77.9% 53.9%; - --destructive-600: 9.7deg 85.2% 62.9%; - --destructive-500: 7.9deg 71.6% 29%; - --destructive-400: 6.7deg 60% 20.6%; - --destructive-300: 7.5deg 51.3% 15.3%; - --destructive-200: 10.9deg 23.4% 9.2%; - --border-button-hover: 180deg 4.9% 23.9%; - --border-button-default: 180deg 7.3% 16.1%; - --border-stronger: 180deg 4.9% 23.9%; - --border-strong: 180deg 7.3% 16.1%; - --border-alternative: 165deg 40% 2%; - --border-control: 180deg 7.3% 16.1%; - --border-overlay: 160deg 7.3% 8%; - --border-secondary: 180deg 17.1% 8%; - --border-muted: 170deg 20% 5.9%; - --border-default: 150deg 13% 9%; - --background-dash-canvas: 180deg 20% 2%; - --background-dash-sidebar: 180deg 20% 2%; - --background-dialog-default: 180deg 20% 2%; - --background-button-default: 154.3deg 14.3% 9.6%; - --background-muted: 160deg 12% 4.9%; - --background-overlay-hover: 168deg 5.2% 19%; - --background-overlay-default: 168deg 9.8% 10%; - --background-surface-400: 154.3deg 14.3% 9.6%; - --background-surface-300: 154.3deg 14.3% 9.6%; - --background-surface-200: 160deg 12% 4.9%; - --background-surface-100: 160deg 23.1% 2.5%; - --background-surface-75: 0deg 0% 100%; - --background-control: 180deg 9.9% 13.9%; - --background-selection: 165deg 8.7% 9%; - --background-alternative-default: 165deg 40% 2%; - --background-alternative-200: 0deg 0% 5.5%; - --background-default: 180deg 20% 2%; - --background-200: 180deg 20% 2%; - --foreground-contrast: 156deg 33.3% 2.9%; - --foreground-muted: 162deg 8.5% 23.1%; - --foreground-lighter: 180deg 6.7% 47.1%; - --foreground-light: 180deg 7.5% 63.5%; - --foreground-default: 0deg 0% 100%; -} diff --git a/packages/ui/build/css/themes/dark-combined.css b/packages/ui/build/css/themes/dark-combined.css deleted file mode 100644 index 5420c2f9bc3..00000000000 --- a/packages/ui/build/css/themes/dark-combined.css +++ /dev/null @@ -1,68 +0,0 @@ -[data-theme='deep-dark'], -.deep-dark, -[data-theme='classic-dark'], -.classic-dark, -[data-theme='dark'], -.dark { - --helpers-os-appearance: Dark; - --code-block-5: 13.8deg 89.7% 69.6%; - --code-block-4: 276.1deg 67.7% 74.5%; - --code-block-3: 83.8deg 61.7% 63.1%; - --code-block-2: 33.2deg 90.3% 75.7%; - --code-block-1: 170.8deg 43.1% 61.4%; - --secondary-default: 247.8deg 100% 70%; - --secondary-400: 248.3deg 54.5% 25.9%; - --secondary-200: 248deg 53.6% 11%; - --brand-link: 155deg 100% 38.6%; - --brand-default: 153.1deg 60.2% 52.7%; - --brand-600: 154.9deg 59.5% 70%; - --brand-500: 154.9deg 100% 19.2%; - --brand-400: 155.5deg 100% 9.6%; - --brand-300: 155.1deg 100% 8%; - --brand-200: 162deg 100% 2%; - --warning-default: 38.9deg 100% 42.9%; - --warning-600: 38.9deg 100% 42.9%; - --warning-500: 34.8deg 90.9% 21.6%; - --warning-400: 33.2deg 100% 14.5%; - --warning-300: 32.3deg 100% 10.2%; - --warning-200: 36.6deg 100% 8%; - --destructive-default: 10.2deg 77.9% 53.9%; - --destructive-600: 9.7deg 85.2% 62.9%; - --destructive-500: 7.9deg 71.6% 29%; - --destructive-400: 6.7deg 60% 20.6%; - --destructive-300: 7.5deg 51.3% 15.3%; - --destructive-200: 10.9deg 23.4% 9.2%; - --border-stronger: 0deg 0% 27.1%; - --border-strong: 0deg 0% 21.2%; - --border-alternative: 0deg 0% 26.7%; - --border-control: 0deg 0% 22.4%; - --border-overlay: 0deg 0% 20%; - --border-secondary: 0deg 0% 14.1%; - --border-muted: 0deg 0% 14.1%; - --border-default: 0deg 0% 18%; - --background-dash-canvas: 0deg 0% 7.1%; - --background-dash-sidebar: 0deg 0% 9%; - --background-dialog-default: 0deg 0% 7.1%; - --background-muted: 0deg 0% 14.1%; - --background-overlay-hover: 0deg 0% 18%; - --background-overlay-default: 0deg 0% 14.1%; - --background-surface-400: 0deg 0% 16.1%; - --background-surface-300: 0deg 0% 16.1%; - --background-surface-200: 0deg 0% 12.9%; - --background-surface-100: 0deg 0% 12.2%; - --background-surface-75: 0deg 0% 9%; - --background-control: 0deg 0% 14.1%; - --background-selection: 0deg 0% 19.2%; - --background-alternative-default: 0deg 0% 5.9%; - --background-default: 0deg 0% 7.1%; - --background-200: 0deg 0% 9%; - --foreground-contrast: 0deg 0% 8.6%; - --foreground-muted: 0deg 0% 30.2%; - --foreground-lighter: 0deg 0% 53.7%; - --foreground-light: 0deg 0% 70.6%; - --foreground-default: 0deg 0% 98%; - --border-button-hover: var(--colors-gray-dark-800); - --border-button-default: var(--colors-gray-dark-700); - --background-button-default: var(--colors-gray-dark-500); - --background-alternative-200: var(--colors-gray-dark-200); -} diff --git a/packages/ui/build/css/themes/dark.css b/packages/ui/build/css/themes/dark.css index 7798d095d1b..4582c05d813 100644 --- a/packages/ui/build/css/themes/dark.css +++ b/packages/ui/build/css/themes/dark.css @@ -1,5 +1,3 @@ -[data-theme='deep-dark'], -.deep-dark, [data-theme='dark'], .dark { --helpers-os-appearance: Dark; diff --git a/packages/ui/internals/tokens/cleanse-css-for-tailwind.js b/packages/ui/internals/tokens/cleanse-css-for-tailwind.js deleted file mode 100644 index fd32e56b91b..00000000000 --- a/packages/ui/internals/tokens/cleanse-css-for-tailwind.js +++ /dev/null @@ -1,41 +0,0 @@ -const glob = require('glob') -const fs = require('fs') - -const cssFiles = glob.sync('build/css/**/*.css') - -function transformCssFiles(precision) { - cssFiles.forEach((file) => { - const css = fs.readFileSync(file, 'utf8') - - let transformedCss = css - - // transform hsl values to degrees, percentages - transformedCss = transformedCss.replace( - /hsl\(([\d.]+)[,\s]+([\d.]+)%[,\s]+([\d.]+)%\)/g, - (match, hue, saturation, lightness) => { - hue = `${roundNumber(hue, precision)}deg` - saturation = `${roundNumber(saturation, precision)}%` - lightness = `${roundNumber(lightness, precision)}%` - return `${hue} ${saturation} ${lightness}` - } - ) - - // remove --core prefix from files - transformedCss = transformedCss.replace(/--core-/g, '--') - - fs.writeFileSync(file, transformedCss) - }) - - function roundNumber(number, precision) { - const rounded = parseFloat(number).toFixed(precision) - const str = rounded.toString() - const match = str.match(/^(\d+)\.(\d+)$/) - if (match && match[2] === '0'.repeat(match[2].length)) { - return match[1] - } - return rounded - } -} - -// Example usage: -transformCssFiles(1) diff --git a/packages/ui/internals/tokens/extract-design-tokens.js b/packages/ui/internals/tokens/extract-design-tokens.js deleted file mode 100644 index 716a2604f69..00000000000 --- a/packages/ui/internals/tokens/extract-design-tokens.js +++ /dev/null @@ -1,54 +0,0 @@ -const fs = require('fs') -const path = require('path') - -// Read the JSON file -try { - const rawData = fs.readFileSync('./tokens/design-tokens.tokens.json') - const jsonData = JSON.parse(rawData) - - // Extract "core" - const coreData = jsonData.core - - // Specify output directories - const coreDirectory = path.join(__dirname, './../../tokens/core') - const themesDirectory = path.join(__dirname, './../../tokens/themes') - - // Check if directories exist, create them if not - if (!fs.existsSync(coreDirectory)) { - fs.mkdirSync(coreDirectory) - console.log(`Created directory: ${coreDirectory}`) - } - - if (!fs.existsSync(themesDirectory)) { - fs.mkdirSync(themesDirectory) - console.log(`Created directory: ${themesDirectory}`) - } - - // Write "core" to a separate JSON file - fs.writeFileSync( - path.join(coreDirectory, 'core.json'), - JSON.stringify({ core: coreData }, null, 2) - ) - console.log('Core file written successfully.') - - // Extract and write each theme to separate JSON files - const themes = jsonData.theme - - Object.keys(themes).forEach((themeName) => { - const themeData = themes[themeName] - const themeFileName = path.join(themesDirectory, `${themeName}.json`) - fs.writeFileSync(themeFileName, JSON.stringify(themeData, null, 2)) - console.log(`Theme file "${themeName}.json" written successfully.`) - }) - - // duplicate the /themes/concept-one.json file to /themes/dark-combined.json - const darkThemePath = path.join(themesDirectory, 'concept-one.json') - const darkCombinedThemePath = path.join(themesDirectory, 'dark-combined.json') - const darkThemeData = fs.readFileSync(darkThemePath) - fs.writeFileSync(darkCombinedThemePath, darkThemeData) - console.log('Dark combined theme file written successfully.') - - console.log('Extraction completed!') -} catch (error) { - console.error('Error:', error.message) -} diff --git a/packages/ui/internals/transform/common/camelCaseHelper.js b/packages/ui/internals/transform/common/camelCaseHelper.js deleted file mode 100644 index 6ee7dec94d7..00000000000 --- a/packages/ui/internals/transform/common/camelCaseHelper.js +++ /dev/null @@ -1,4 +0,0 @@ -const changeCase = require('change-case') - -module.exports = (name) => - changeCase.camelCase(name, { transform: changeCase.camelCaseTransformMerge }) diff --git a/packages/ui/internals/transform/common/colorToHex8.js b/packages/ui/internals/transform/common/colorToHex8.js deleted file mode 100644 index 64096af5c23..00000000000 --- a/packages/ui/internals/transform/common/colorToHex8.js +++ /dev/null @@ -1,11 +0,0 @@ -const TinyColor = require('@ctrl/tinycolor') - -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'color' - }, - transformer: function ({ value }) { - return `${new TinyColor.TinyColor(value).toHex8String()}` - }, -} diff --git a/packages/ui/internals/transform/common/colorToRgbaString.js b/packages/ui/internals/transform/common/colorToRgbaString.js deleted file mode 100644 index bc97f942a46..00000000000 --- a/packages/ui/internals/transform/common/colorToRgbaString.js +++ /dev/null @@ -1,11 +0,0 @@ -const TinyColor = require('@ctrl/tinycolor') - -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'color' - }, - transformer: function ({ value }) { - return `${new TinyColor.TinyColor(value).toRgbString()}` - }, -} diff --git a/packages/ui/internals/transform/common/copyFileOrFolder.js b/packages/ui/internals/transform/common/copyFileOrFolder.js deleted file mode 100644 index 3a8c2793f2a..00000000000 --- a/packages/ui/internals/transform/common/copyFileOrFolder.js +++ /dev/null @@ -1,16 +0,0 @@ -const fs = require('fs-extra') - -module.exports = { - do: function (dictionary, config) { - config.options.copyFilesAction.forEach(({ destination, origin }) => { - console.log(`Copying ${origin} to ${destination}`) - fs.copySync(origin, destination) - }) - }, - undo: function (dictionary, config) { - config.options.copyFilesAction.forEach(({ destination, origin }) => { - console.log(`Cleaning ${destination}`) - fs.removeSync(destination) - }) - }, -} diff --git a/packages/ui/internals/transform/web/createPropertyFormatter.js b/packages/ui/internals/transform/web/createPropertyFormatter.js deleted file mode 100644 index a3c129f4ee0..00000000000 --- a/packages/ui/internals/transform/web/createPropertyFormatter.js +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with - * the License. A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -const defaultFormatting = { - prefix: '', - commentStyle: 'long', - indentation: '', - separator: ' =', - suffix: ';', -} - -/** - * Creates a function that can be used to format a property. This can be useful - * to use as the function on `dictionary.allTokens.map`. The formatting - * is configurable either by supplying a `format` option or a `formatting` object - * which uses: prefix, indentation, separator, suffix, and commentStyle. - * @memberof module:formatHelpers - * @example - * ```javascript - * StyleDictionary.registerFormat({ - * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { - * const { outputReferences } = options; - * const formatProperty = createPropertyFormatter({ - * outputReferences, - * dictionary, - * format: 'css' - * }); - * return dictionary.allTokens.map(formatProperty).join('\n'); - * } - * }); - * ``` - * @param {Object} options - * @param {Boolean} options.outputReferences - Whether or not to output references. You will want to pass this from the `options` object sent to the formatter function. - * @param {Dictionary} options.dictionary - The dictionary object sent to the formatter function - * @param {String} options.format - Available formats are: 'css', 'sass', 'less', and 'stylus'. If you want to customize the format and can't use one of those predefined formats, use the `formatting` option - * @param {Object} options.formatting - Custom formatting properties that define parts of a declaration line in code. The configurable strings are: prefix, indentation, separator, suffix, and commentStyle. Those are used to generate a line like this: `${indentation}${prefix}${prop.name}${separator} ${prop.value}${suffix}` - * @returns {Function} - */ -function createPropertyFormatter({ outputReferences, dictionary, format, formatting = {} }) { - let { prefix, commentStyle, indentation, separator, suffix } = Object.assign( - {}, - defaultFormatting, - formatting - ) - - switch (format) { - case 'css': - prefix = '--' - indentation = ' ' - separator = ':' - break - case 'sass': - prefix = '$' - commentStyle = 'short' - indentation = '' - separator = ':' - break - case 'less': - prefix = '@' - commentStyle = 'short' - indentation = '' - separator = ':' - break - case 'stylus': - prefix = '$' - commentStyle = 'short' - indentation = '' - separator = '=' - break - } - - return function (prop) { - let to_ret_prop = `${indentation}${prefix}${prop.name}${separator} ` - .replace('--dark-', '--') - .replace('--light-', '--') - let value = prop.value - - /** - * A single value can have multiple references either by interpolation: - * "value": "{size.border.width.value} solid {color.border.primary.value}" - * or if the value is an object: - * "value": { - * "size": "{size.border.width.value}", - * "style": "solid", - * "color": "{color.border.primary.value"} - * } - * This will see if there are references and if there are, replace - * the resolved value with the reference's name. - */ - if (outputReferences && dictionary.usesReference(prop.original.value)) { - // Formats that use this function expect `value` to be a string - // or else you will get '[object Object]' in the output - if (typeof value === 'string') { - const refs = dictionary.getReferences(prop.original.value) - refs.forEach((ref) => { - // value should be a string that contains the resolved reference - // because Style Dictionary resolved this in the resolution step. - // Here we are undoing that by replacing the value with - // the reference's name - if (ref.value && ref.name) { - value = value.replace(ref.value, function () { - if (format === 'css') { - return `var(${prefix}${ref.name})` - } else { - return `${prefix}${ref.name}` - } - }) - } - }) - } - } - - to_ret_prop += prop.attributes.category === 'asset' ? `"${value}"` : value - - if (format == 'sass' && prop.themeable === true) { - to_ret_prop += ' !default' - } - - to_ret_prop += suffix - - if (prop.comment && commentStyle !== 'none') { - if (commentStyle === 'short') { - to_ret_prop = to_ret_prop.concat(` // ${prop.comment}`) - } else { - to_ret_prop = to_ret_prop.concat(` /* ${prop.comment} */`) - } - } - - return to_ret_prop - } -} - -module.exports = createPropertyFormatter diff --git a/packages/ui/internals/transform/web/fileHeader.js b/packages/ui/internals/transform/web/fileHeader.js deleted file mode 100644 index c48930e2916..00000000000 --- a/packages/ui/internals/transform/web/fileHeader.js +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with - * the License. A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -// no-op default -const defaultFileHeader = (arr) => arr - -const lineSeparator = '\n' -const defaultFormatting = { - lineSeparator, - prefix: ' * ', - header: `/**${lineSeparator}`, - footer: `${lineSeparator} */${lineSeparator}${lineSeparator}`, -} - -/** - * - * This is for creating the comment at the top of generated files with the generated at date. - * It will use the custom file header if defined on the configuration, or use the - * default file header. - * @memberof module:formatHelpers - * @param {Object} options - * @param {File} options.file - The file object that is passed to the formatter. - * @param {String} options.commentStyle - The only options are 'short' and 'xml', which will use the // or \ style comments respectively. Anything else will use \/\* style comments. - * @param {Object} options.formatting - Custom formatting properties that define parts of a comment in code. The configurable strings are: prefix, lineSeparator, header, and footer. - * @returns {String} - * @example - * ```js - * StyleDictionary.registerFormat({ - * name: 'myCustomFormat', - * formatter: function({ dictionary, file }) { - * return fileHeader({file, 'short') + - * dictionary.allTokens.map(token => `${token.name} = ${token.value}`) - * .join('\n'); - * } - * }); - * ``` - */ -function fileHeader({ file = {}, commentStyle, formatting = {} }) { - // showFileHeader is true by default - let showFileHeader = true - if (file.options && typeof file.options.showFileHeader !== 'undefined') { - showFileHeader = file.options.showFileHeader - } - - // Return empty string if the showFileHeader is false - if (!showFileHeader) return '' - - let fn = defaultFileHeader - if (file.options && typeof file.options.fileHeader === 'function') { - fn = file.options.fileHeader - } - - // default header - const defaultHeader = ['Do not edit directly', `Generated on ${new Date().toString()}`] - - let { prefix, lineSeparator, header, footer } = Object.assign({}, defaultFormatting, formatting) - - if (commentStyle === 'short') { - prefix = '// ' - header = `${lineSeparator}` - footer = `${lineSeparator}${lineSeparator}` - } else if (commentStyle === 'xml') { - prefix = ' ' - header = `` - } - - return `${header}${fn(defaultHeader) - .map((line) => `${prefix}${line}`) - .join(lineSeparator)}${footer}` -} - -module.exports = fileHeader diff --git a/packages/ui/internals/transform/web/filterWeb.js b/packages/ui/internals/transform/web/filterWeb.js deleted file mode 100644 index f03fc479b0d..00000000000 --- a/packages/ui/internals/transform/web/filterWeb.js +++ /dev/null @@ -1,11 +0,0 @@ -const acceptedTypes = [ - 'color', - 'dimension', - 'font', - 'custom-radius', - 'custom-fontStyle', - 'custom-shadow', - 'custom-gradient', -] - -module.exports = (token) => acceptedTypes.includes(token.type) diff --git a/packages/ui/internals/transform/web/formatCss.js b/packages/ui/internals/transform/web/formatCss.js deleted file mode 100644 index 17a70e1d206..00000000000 --- a/packages/ui/internals/transform/web/formatCss.js +++ /dev/null @@ -1,48 +0,0 @@ -const formattedVariables = require('./formattedVariables') -const fileHeader = require('./fileHeader') - -const filteredTokens = (dictionary, filterFn) => { - const filtered = dictionary.allTokens.filter((token) => filterFn(token)) - return { - ...dictionary, - ...{ - allProperties: filtered, - allTokens: filtered, - }, - } -} - -module.exports = ({ dictionary, options, file }) => { - const opts = options ?? {} - const { outputReferences } = opts - const groupedTokens = { - // if you export the prefixes use token.path[0] instead of [1] - light: filteredTokens(dictionary, (token) => token.path[1].toLowerCase() === 'light'), - dark: filteredTokens(dictionary, (token) => token.path[1].toLowerCase() === 'dark'), - rest: filteredTokens( - dictionary, - (token) => !['light', 'dark'].includes(token.path[1].toLowerCase()) - ), - } - - return ( - fileHeader({ file }) + - ':root {\n' + - formattedVariables({ format: 'css', dictionary: groupedTokens.rest, outputReferences }) + - '\n}\n\n' + - '@media (prefers-color-scheme: light) {\n' + - ' :root {\n' + - formattedVariables({ format: 'css', dictionary: groupedTokens.light, outputReferences }) + - '\n }\n}\n\n' + - '@media (prefers-color-scheme: dark) {\n' + - ' :root {\n' + - formattedVariables({ format: 'css', dictionary: groupedTokens.dark, outputReferences }) + - '\n }\n}\n\n' + - 'html[data-theme="light"] {\n' + - formattedVariables({ format: 'css', dictionary: groupedTokens.light, outputReferences }) + - '\n}\n\n' + - 'html[data-theme="dark"] {\n' + - formattedVariables({ format: 'css', dictionary: groupedTokens.dark, outputReferences }) + - '\n}\n' - ) -} diff --git a/packages/ui/internals/transform/web/formattedVariables.js b/packages/ui/internals/transform/web/formattedVariables.js deleted file mode 100644 index 195d6a9f83a..00000000000 --- a/packages/ui/internals/transform/web/formattedVariables.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with - * the License. A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -const createPropertyFormatter = require('./createPropertyFormatter') -const sortByReference = require('./sortByReference') - -const defaultFormatting = { - lineSeparator: '\n', -} - -/** - * - * This is used to create lists of variables like Sass variables or CSS custom properties - * @memberof module:formatHelpers - * @param {Object} options - * @param {String} options.format - What type of variables to output. Options are: css, sass, less, and stylus - * @param {Object} options.dictionary - The dictionary object that gets passed to the formatter method. - * @param {Boolean} options.outputReferences - Whether or not to output references - * @param {Object} options.formatting - Custom formatting properties that define parts of a declaration line in code. This will get passed to `formatHelpers.createPropertyFormatter` and used for the `lineSeparator` between lines of code. - * @returns {String} - * @example - * ```js - * StyleDictionary.registerFormat({ - * name: 'myCustomFormat', - * formatter: function({ dictionary, options }) { - * return formattedVariables('less', dictionary, options.outputReferences); - * } - * }); - * ``` - */ -function formattedVariables({ format, dictionary, outputReferences = false, formatting = {} }) { - let { allTokens } = dictionary - - const { lineSeparator } = Object.assign({}, defaultFormatting, formatting) - - // Some languages are imperative, meaning a variable has to be defined - // before it is used. If `outputReferences` is true, check if the token - // has a reference, and if it does send it to the end of the array. - // We also need to account for nested references, a -> b -> c. They - // need to be defined in reverse order: c, b, a so that the reference always - // comes after the definition - if (outputReferences) { - // note: using the spread operator here so we get a new array rather than - // mutating the original - allTokens = [...allTokens].sort(sortByReference(dictionary)) - } - - return allTokens - .map(createPropertyFormatter({ outputReferences, dictionary, format, formatting })) - .filter(function (strVal) { - return !!strVal - }) - .join(lineSeparator) -} - -module.exports = formattedVariables diff --git a/packages/ui/internals/transform/web/index.js b/packages/ui/internals/transform/web/index.js deleted file mode 100644 index f512415c131..00000000000 --- a/packages/ui/internals/transform/web/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const StyleDictionary = require('style-dictionary') - -module.exports = { - transform: { - 'size/px': require('./sizePx'), - 'web/shadow': require('./webShadows'), - 'web/radius': require('./webRadius'), - 'web/padding': require('./webPadding'), - 'web/font': require('./webFont'), - 'web/gradient': require('./webGradient'), - 'color/hex8ToRgba': require('../common/colorToRgbaString'), - }, - transformGroup: { - 'custom/css': StyleDictionary.transformGroup.css.concat([ - 'size/px', - 'web/shadow', - 'web/radius', - 'web/padding', - 'web/font', - 'web/gradient', - 'color/hex8ToRgba', - 'color/rgb', - ]), - }, - format: { - 'custom/css': require('./formatCss'), - }, - action: {}, -} diff --git a/packages/ui/internals/transform/web/sizePx.js b/packages/ui/internals/transform/web/sizePx.js deleted file mode 100644 index 04fe72999eb..00000000000 --- a/packages/ui/internals/transform/web/sizePx.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'dimension' && token.value !== 0 - }, - transformer: function (token) { - return `${token.value}px` - }, -} diff --git a/packages/ui/internals/transform/web/sortByReference.js b/packages/ui/internals/transform/web/sortByReference.js deleted file mode 100644 index 5dd4af79b99..00000000000 --- a/packages/ui/internals/transform/web/sortByReference.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with - * the License. A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -/** - * A function that returns a sorting function to be used with Array.sort that - * will sort the allTokens array based on references. This is to make sure - * if you use output references that you never use a reference before it is - * defined. - * @memberof module:formatHelpers - * @example - * ```javascript - * dictionary.allTokens.sort(sortByReference(dictionary)) - * ``` - * @param {Dictionary} dictionary - * @returns {Function} - */ -function sortByReference(dictionary) { - // The sorter function is recursive to account for multiple levels of nesting - function sorter(a, b) { - const aComesFirst = -1 - const bComesFirst = 1 - - // If token a uses a reference and token b doesn't, b might come before a - // read on.. - if (a.original && dictionary.usesReference(a.original.value)) { - // Both a and b have references, we need to see if the reference each other - if (b.original && dictionary.usesReference(b.original.value)) { - const aRefs = dictionary.getReferences(a.original.value) - const bRefs = dictionary.getReferences(b.original.value) - - aRefs.forEach((aRef) => { - // a references b, we want b to come first - if (aRef.name === b.name) { - return bComesFirst - } - }) - - bRefs.forEach((bRef) => { - // ditto but opposite - if (bRef.name === a.name) { - return aComesFirst - } - }) - - // both a and b have references and don't reference each other - // we go further down the rabbit hole (reference chain) - return sorter(aRefs[0], bRefs[0]) - // a has a reference and b does not: - } else { - return bComesFirst - } - // a does not have a reference it should come first regardless if b has one - } else { - return aComesFirst - } - } - - return sorter -} - -module.exports = sortByReference diff --git a/packages/ui/internals/transform/web/webFont.js b/packages/ui/internals/transform/web/webFont.js deleted file mode 100644 index 4bebf5e80f5..00000000000 --- a/packages/ui/internals/transform/web/webFont.js +++ /dev/null @@ -1,15 +0,0 @@ -const notDefault = (value, defaultValue) => (value !== defaultValue ? value : '') - -const fontFamily = ({ fontFamily }, { fontFamilies } = {}) => - fontFamilies && fontFamilies[fontFamily] ? fontFamilies[fontFamily] : fontFamily - -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'custom-fontStyle' - }, - transformer: function ({ value: font }, { options }) { - // font: font-style font-variant font-weight font-size/line-height font-family; - return `${notDefault(font.fontStretch, 'normal')} ${notDefault(font.fontStyle, 'normal')} ${font.fontWeight} ${font.fontSize}/${font.lineHeight} ${fontFamily(font, options)}`.trim() - }, -} diff --git a/packages/ui/internals/transform/web/webGradient.js b/packages/ui/internals/transform/web/webGradient.js deleted file mode 100644 index 1325b271a0a..00000000000 --- a/packages/ui/internals/transform/web/webGradient.js +++ /dev/null @@ -1,21 +0,0 @@ -const TinyColor = require('@ctrl/tinycolor') - -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'custom-gradient' - }, - transformer: function ({ value }) { - const stopsString = value.stops - .map((stop) => { - return `${new TinyColor.TinyColor(stop.color).toRgbString()} ${stop.position * 100}%` - }) - .join(', ') - if (value.gradientType === 'linear') { - return `linear-gradient(${value.rotation}deg, ${stopsString})` - } - if (value.gradientType === 'radial') { - return `radial-gradient(${stopsString})` - } - }, -} diff --git a/packages/ui/internals/transform/web/webPadding.js b/packages/ui/internals/transform/web/webPadding.js deleted file mode 100644 index 29826a90911..00000000000 --- a/packages/ui/internals/transform/web/webPadding.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'custom-spacing' - }, - transformer: ({ value: { top, left, bottom, right } }) => { - if ([bottom, left, right].every((v) => v === top)) { - return `${top}px` - } - return `${top}px ${right}px ${bottom}px ${left}px` - }, -} diff --git a/packages/ui/internals/transform/web/webRadius.js b/packages/ui/internals/transform/web/webRadius.js deleted file mode 100644 index 0b72b06d01a..00000000000 --- a/packages/ui/internals/transform/web/webRadius.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'custom-radius' - }, - transformer: function ({ value }) { - if ([value.topRight, value.bottomLeft, value.bottomRight].every((v) => v === value.topLeft)) { - return `${value.topLeft}px` - } - return `${value.topLeft}px ${value.topRight}px ${value.bottomLeft}px ${value.bottomRight}px` - }, -} diff --git a/packages/ui/internals/transform/web/webShadows.js b/packages/ui/internals/transform/web/webShadows.js deleted file mode 100644 index 3268c2232fd..00000000000 --- a/packages/ui/internals/transform/web/webShadows.js +++ /dev/null @@ -1,11 +0,0 @@ -const TinyColor = require('@ctrl/tinycolor') - -module.exports = { - type: 'value', - matcher: function (token) { - return token.type === 'custom-shadow' && token.value !== 0 - }, - transformer: function ({ value }) { - return `${value.shadowType === 'innerShadow' ? 'inset ' : ''}${value.offsetX}px ${value.offsetY}px ${value.radius}px ${value.spread}px ${new TinyColor.TinyColor(value.color).toRgbString()}` - }, -} diff --git a/packages/ui/package.json b/packages/ui/package.json index d61942413fc..d421ee36a34 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -8,11 +8,7 @@ "scripts": { "preinstall": "npx only-allow pnpm", "typecheck": "tsc --noEmit", - "generate-demo-tailwind-classes": "node internals/tokens/generate-demo-tailwind-classes.js", - "cleanse-css-for-tailwind": "node internals/tokens/cleanse-css-for-tailwind.js", - "transform-tokens": "node ./transformTokens.js", - "extract-design-tokens": "node internals/tokens/extract-design-tokens.js", - "generate-styles": "pnpm run extract-design-tokens && pnpm run transform-tokens && pnpm run cleanse-css-for-tailwind && pnpm run generate-demo-tailwind-classes", + "generate-tailwind-classes": "tsx ./scripts/generate-tailwind-classes.ts", "clean": "rimraf node_modules .turbo tsconfig.tsbuildinfo", "test": "vitest", "test:ci": "vitest --run --coverage", @@ -73,7 +69,6 @@ "vaul": "^0.9.9" }, "devDependencies": { - "@ctrl/tinycolor": "^3.4.0", "@testing-library/jest-dom": "^6.6.0", "@testing-library/react": "^16.0.0", "@types/lodash": "4.17.5", @@ -83,9 +78,8 @@ "@types/react-dom": "catalog:", "@vitest/coverage-v8": "catalog:", "config": "workspace:*", - "glob": "^8.1.0", - "style-dictionary": "^3.7.1", "tsconfig": "workspace:*", + "tsx": "catalog:", "typescript": "catalog:", "vite": "catalog:", "vitest": "catalog:" diff --git a/packages/ui/internals/tokens/generate-demo-tailwind-classes.js b/packages/ui/scripts/generate-tailwind-classes.ts similarity index 96% rename from packages/ui/internals/tokens/generate-demo-tailwind-classes.js rename to packages/ui/scripts/generate-tailwind-classes.ts index e609e8251f6..c2e61afae35 100644 --- a/packages/ui/internals/tokens/generate-demo-tailwind-classes.js +++ b/packages/ui/scripts/generate-tailwind-classes.ts @@ -9,7 +9,7 @@ const color = require('./../../build/css/tw-extend/color') * @param {string} file * Name of file to be created */ -function writeJsFile(file) { +function writeJsFile(file: string) { const backgrounds = Object.keys(color).filter((x) => x.includes('background-')) const borders = Object.keys(color).filter((x) => x.includes('border-')) const texts = Object.keys(color).filter((x) => x.includes('foreground-')) @@ -25,7 +25,7 @@ function writeJsFile(file) { // console.log('Example tailwind classes: ') - function santizieDefaults(x) { + function santizieDefaults(x: string) { // console.log(x) let value = x value = value.replace('-DEFAULT', '') diff --git a/packages/ui/shadcn.css b/packages/ui/shadcn.css deleted file mode 100644 index 28b676e9740..00000000000 --- a/packages/ui/shadcn.css +++ /dev/null @@ -1,30 +0,0 @@ -@layer base { - :root { - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - .dark { - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } -} -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} diff --git a/packages/ui/tailwind-theming.md b/packages/ui/tailwind-theming.md deleted file mode 100644 index 6d572aa15a9..00000000000 --- a/packages/ui/tailwind-theming.md +++ /dev/null @@ -1,139 +0,0 @@ -# Tailwind theming - -Supabase is migrating to use tailwind classes that use CSS properties. -This is to support the concept of theming, so that: - -1. It is easy for the team to update the theme without changing 100s of files. -2. We could offer custom or additional themes in future. - -## Primitives - -We (the Supabase Design team ✨) define primitive color values using Figma Variables. - -- `foreground` -- `background` -- `border` -- `brand` (could become primary) -- `destructive` -- `warning` - -These values are exported from Figma as a .json file and transformed into tailwind utilities through some scripts under `packages/ui/internals/tokens`. - -Primitives work the same way as any other color. They should follow some basic usage patterns such as _foreground_ on text and _background_ on surfaces, but they can also be combined with any tailwind utility to achieve more advanced layouts. - -For example, `foreground-light` can also be applied on borders and backgrounds as `border-foreground-light` and `bg-foreground-light`, if needed. - -Similarly, background and border primitives can be used on other tailwind utilities. - -``` -border-surface-100 -bg-border-overlay -text-background-surface-100 -``` - -## Usage - -The following tailwind classes are a combination of tailwind utilities and our primitives. They're under development and are likely to change but most are now in use and will probably be permanent fixtures. - -### Foreground (Text) - -| Value | Usage | -| ---------------------- | -------------------------------------- | -| `foreground-{DEFAULT}` | Default text (**DEFAULT** is optional) | -| `foreground-light` | Light text | -| `foreground-lighter` | Lighter text | -| `foreground-muted` | Muted text | - -Examples: - -``` -text-foreground -text-foreground-light -text-foreground-lighter -text-foreground-muted -bg-foreground-light -``` - ---- - -### Background - -| Value | Usage | -| ------------------------------- | ------------------------------------------------------------ | -| `{background}-{DEFAULT}` | Main body background (**DEFAULT** is optional) | -| `{background}-surface-100` | Panels and surfaces on the same level of the main background | -| `{background}-surface-200` | Surfaces that overlap the main content (ex. dropdowns) | -| `{background}-surface-300` | Surfaces that are stacked above {background}-surface-200 | -| `{background}-alternative` | Alternative background (inverted) | -| `{background}-overlay` | Overlays, Dropdowns, Popovers | -| `{background}-control` | Inputs, Radios, Checkboxes | -| `{background}-button-{DEFAULT}` | Button default | - -The `background` part can be omitted when used on the `bg` tailwind utility. - -Examples: - -``` -bg-surface-100 -bg-overlay -bg-alternative -text-background-surface-100 -``` - ---- - -### Border - -| Value | Usage | -| ------------------------- | ---------------------------------------- | -| `border-{DEFAULT}` | Default border (**DEFAULT** is optional) | -| `border-secondary` | Secondary border | -| `border-alternative` | Alternative border (inverted) | -| `border-overlay` | Overlays, Dropdowns, Popovers | -| `border-control` | Inputs, Radios, Checkboxes | -| `border-strong` | Hover, Focus | -| `border-stronger` | Highlighted border | -| `border-button-{DEFAULT}` | Button default border | -| `border-button-hover` | Button default border hover | - -Examples: - -``` -border-overlay -border-alternative -text-border-control -``` - -### Brand - -| Value | -| --------- | -| `200` | -| `300` | -| `400` | -| `500` | -| `DEFAULT` | -| `600` | -| `button` | - -### Destructive - -| Value | -| --------- | -| `200` | -| `300` | -| `400` | -| `500` | -| `DEFAULT` | -| `600` | - -### Warning - -| Value | -| --------- | -| `200` | -| `300` | -| `400` | -| `500` | -| `DEFAULT` | -| `600` | diff --git a/packages/ui/tailwind.config.js b/packages/ui/tailwind.config.js new file mode 100644 index 00000000000..fd65503e32a --- /dev/null +++ b/packages/ui/tailwind.config.js @@ -0,0 +1 @@ +// Placeholder so that VSCode tailwind extension enables intellisense in this project diff --git a/packages/ui/transformTokens.js b/packages/ui/transformTokens.js deleted file mode 100644 index f9f2319ef89..00000000000 --- a/packages/ui/transformTokens.js +++ /dev/null @@ -1,298 +0,0 @@ -const glob = require('glob') - -const sourceFiles = glob.sync(`tokens/core/**/*.json`) -const themeFiles = glob.sync(`tokens/themes/**/*.json`) - -const StyleDictionary = require('style-dictionary') -const deepMerge = require('deepmerge') -const webConfig = require('./internals/transform/web/index.js') - -const Color = require('color') - -StyleDictionary.registerTransform({ - name: 'size/px', - type: 'value', - matcher: (token) => { - return (token.unit === 'pixel' || token.type === 'dimension') && token.value !== 0 - }, - transformer: (token) => { - return `${token.value}px` - }, -}) - -StyleDictionary.registerTransform({ - name: 'size/percent', - type: 'value', - matcher: (token) => { - return token.unit === 'percent' && token.value !== 0 - }, - transformer: (token) => { - return `${token.value}%` - }, -}) - -StyleDictionary.registerFilter({ - name: 'validToken', - matcher: function (token) { - return [ - 'dimension', - 'string', - 'number', - 'color', - 'custom-spacing', - 'custom-gradient', - 'custom-fontStyle', - 'custom-radius', - 'custom-shadow', - ].includes(token.type) - }, -}) - -/** - * Custom format that generate tailwind color config based on css variables - */ -StyleDictionary.registerFormat({ - name: 'tw/css-variables', - formatter({ dictionary }) { - // console.log('dictionary', dictionary) - return ( - 'module.exports = ' + - `{\n${dictionary.allProperties - .map((token) => { - const value = formatTailwindValue(token.type, token.value) - return `"${token.path.slice(0).join('-')}": { - cssVariable: "var(--${token.name})", - value: "${value}" -}` - .replace('core-', '') // remove core prefix - .replace('default', 'DEFAULT') // replace default with DEFAULT for tailwind config - }) - .join(',\n')}\n}` - ) - }, -}) - -/** - * Custom format that generates tailwind friendly colors so we can add - * in the config later - * https://tailwindcss.com/docs/customizing-colors#using-css-variables - */ -StyleDictionary.registerTransform({ - name: 'color/rgb', - type: 'value', - matcher: (prop) => prop.type === 'color', - transformer: (prop) => { - if (!prop.original.value) { - console.error(`No color value detected for ${prop.name}.`) - } - - if (prop.original.value === '#') { - console.error(`Only "#" as value for ${prop.name}. Must use a correct format like "#FFF"`) - } - - const color = Color(prop?.original?.value) - - if (!color) { - console.error('No color') - } - - // console.log(color) - - const hsl = Color(prop?.original?.value).hsl() - return hsl.string() - }, -}) - -function fileNameCleaner(fileName) { - return fileName.split('/').pop().replace('.json', '') -} - -const formatTailwindValue = (tokenType, value) => { - let formattedValue - switch (tokenType) { - case 'color': - default: - formattedValue = value - } - return formattedValue -} - -/** - * uncomment tailwind types as they become available - */ -const supportedTokenTypeList = [ - // 'spacing', - // 'sizing', - // 'borderRadius', - // 'borderWidth', - 'color', - // 'opacity', - // 'fontFamilies', - // 'lineHeights', - // 'letterSpacing', - // 'paragraphSpacing', - // 'fontWeights', - // 'fontSizes', - // 'textCase', - // 'textDecoration', -] - -/** - * Returns the files configuration - * for generating separated tailwind files. - */ -function getConfigTailwindFilesByType(typeList) { - console.log('\n') - console.log('tailwind typeList', typeList) - return typeList.map((typeName) => { - return { - destination: `tw-extend/${typeName}.js`, - format: 'tw/css-variables', - filter: { - type: typeName, - }, - } - }) -} - -/** - * BUILD SOURCE FILES - */ - -console.log(`\n`) - -console.log('Building Core Styles...') - -console.log(`\n`) - -sourceFiles.map(function (filePath) { - console.log(filePath) - - const StyleDictionaryExtended = StyleDictionary.extend({ - ...deepMerge.all([webConfig]), - source: [filePath], - platforms: { - css: { - transformGroup: 'custom/css', - buildPath: 'build/css/', - files: [ - { - destination: 'source/global.css', - format: 'css/variables', - filter: 'validToken', - options: { - showFileHeader: false, - }, - }, - ], - }, - // 'json-flat': { - // transformGroup: 'js', - // buildPath: 'build/json/', - // files: [ - // { - // destination: 'core-styles.json', - // format: 'json/flat', - // filter: 'validToken', - // }, - // ], - // }, - }, - }) - - StyleDictionaryExtended.buildAllPlatforms() - // console.log('StyleDictionaryExtended', StyleDictionaryExtended) - // Check if properties and all are defined - if (StyleDictionaryExtended.properties && StyleDictionaryExtended.properties.all) { - // Modify and print the CSS with the prefix removed - const modifiedCSS = - StyleDictionaryExtended.properties.all.reduce((result, prop) => { - const modifiedName = prop.path.join('-').replace(/^core-/, '') - result += ` --${modifiedName}: ${prop.original.value};\n` - return result - }, ':root {\n') + '}' - - console.log(modifiedCSS) - } else { - console.error('Error: Unable to retrieve properties from StyleDictionaryExtended.') - } -}) - -console.log(`\n`) - -console.log(`Building Theme Styles...`) - -console.log(`\n`) - -console.log(themeFiles) - -themeFiles.map(function (filePath, i) { - let fileName = fileNameCleaner(filePath) - - const isCombinedDark = fileName === 'dark-combined' ? true : false - - // rename concept-one to dark-new - fileName = fileName === 'dark' ? 'classic-dark' : fileName - fileName = fileName === 'concept-one' ? 'dark' : fileName - - const selector = isCombinedDark - ? `[data-theme='deep-dark'], -.deep-dark, -[data-theme='classic-dark'], -.classic-dark, -[data-theme='dark'], -.dark` - : fileName === 'dark' - ? `[data-theme='deep-dark'], -.deep-dark, -[data-theme='${fileName}'], -.${fileName}` - : `[data-theme='${fileName}'], -.${fileName}` - - let configTailwindFilesByType = [] - - if (i === 0) { - // runs on first theme file - console.log(`\n`) - console.log(`Generating Tailwind Props ✨`) - configTailwindFilesByType = getConfigTailwindFilesByType(supportedTokenTypeList) - } - - const StyleDictionaryExtended = StyleDictionary.extend({ - ...deepMerge.all([webConfig]), - source: [...sourceFiles, filePath], - platforms: { - css: { - transformGroup: 'custom/css', - buildPath: 'build/css/', - files: [ - { - destination: `themes/${fileName}.css`, - format: 'css/variables', - filter: 'validToken', - options: { - showFileHeader: false, - outputReferences: true, - selector: selector, - }, - filter: (token) => token.filePath === filePath, - }, - ...configTailwindFilesByType, - ], - }, - // 'json-flat': { - // transformGroup: 'js', - // buildPath: 'build/json/', - // files: [ - // { - // destination: `themes/${fileName}-styles.json`, - // format: 'json/flat', - // filter: 'validToken', - // }, - // ], - // }, - }, - }) - StyleDictionaryExtended.buildAllPlatforms() -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7084d25e503..3f4bea60eae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2603,9 +2603,6 @@ importers: specifier: ^0.9.9 version: 0.9.9(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: - '@ctrl/tinycolor': - specifier: ^3.4.0 - version: 3.6.1 '@testing-library/jest-dom': specifier: ^6.6.0 version: 6.6.3 @@ -2633,15 +2630,12 @@ importers: config: specifier: workspace:* version: link:../config - glob: - specifier: ^8.1.0 - version: 8.1.0 - style-dictionary: - specifier: ^3.7.1 - version: 3.8.0 tsconfig: specifier: workspace:* version: link:../tsconfig + tsx: + specifier: 'catalog:' + version: 4.20.3 typescript: specifier: 'catalog:' version: 6.0.2 @@ -3532,10 +3526,6 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@ctrl/tinycolor@3.6.1': - resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} - engines: {node: '>=10'} - '@dagrejs/dagre@1.0.4': resolution: {integrity: sha512-jrEore+HhW1yg1Rsd9H1PPMcoEOD4bVh0WCXc6GqzyzubnJj4GaWGg8ETOrskTd/3n/g5LOzumGM4CCgpNLJNw==} @@ -12434,10 +12424,6 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - fs-extra@11.3.2: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} @@ -12598,11 +12584,6 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} @@ -13686,9 +13667,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} - jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -17408,11 +17386,6 @@ packages: structured-clone-es@2.0.0: resolution: {integrity: sha512-5UuAHmBLXYPCl22xWJrFuGmIhBKQzxISPVz6E7nmTmTcAOpUzlbjKJsRrCE4vADmMQ0dzeCnlWn9XufnAGf76Q==} - style-dictionary@3.8.0: - resolution: {integrity: sha512-wHlB/f5eO3mDcYv6WtOz6gvQC477jBKrwuIXe+PtHskTCBsJdAOvL8hCquczJxDui2TnwpeNE+2msK91JJomZg==} - engines: {node: '>=12.0.0'} - hasBin: true - style-mod@4.1.2: resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} @@ -17647,9 +17620,6 @@ packages: resolution: {integrity: sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==} engines: {node: ^16.14.0 || >= 17.3.0} - tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tinyexec@1.0.4: resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} @@ -20330,8 +20300,6 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} - '@ctrl/tinycolor@3.6.1': {} - '@dagrejs/dagre@1.0.4': dependencies: '@dagrejs/graphlib': 2.1.13 @@ -30509,12 +30477,6 @@ snapshots: fresh@2.0.0: {} - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 @@ -30695,14 +30657,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - glob@8.1.0: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.8 - once: 1.4.0 - glob@9.3.5: dependencies: fs.realpath: 1.0.0 @@ -31855,8 +31809,6 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.2.0: {} - jsonfile@6.1.0: dependencies: universalify: 2.0.0 @@ -36870,18 +36822,6 @@ snapshots: structured-clone-es@2.0.0: {} - style-dictionary@3.8.0: - dependencies: - chalk: 4.1.2 - change-case: 4.1.2 - commander: 8.3.0 - fs-extra: 10.1.0 - glob: 7.2.3 - json5: 2.2.3 - jsonc-parser: 3.2.0 - lodash: 4.18.1 - tinycolor2: 1.6.0 - style-mod@4.1.2: {} style-to-js@1.1.16: @@ -37148,8 +37088,6 @@ snapshots: tinyclip@0.1.12: {} - tinycolor2@1.6.0: {} - tinyexec@1.0.4: {} tinyglobby@0.2.15: