diff --git a/apps/design-system/components/color-palette.tsx b/apps/design-system/components/color-palette.tsx index 2f8f3012bd7..024b7312f8a 100644 --- a/apps/design-system/components/color-palette.tsx +++ b/apps/design-system/components/color-palette.tsx @@ -56,11 +56,11 @@ const ColorPalette = () => {
{name}
{SCALE_STEPS.map((step) => { - const reference = `var(--colors-${slug}${step})` + const reference = `var(--color-${slug}-${step * 100})` const isCopied = copied === reference return ( ) diff --git a/apps/design-system/content/docs/color-usage.mdx b/apps/design-system/content/docs/color-usage.mdx index b8f5f16b555..b62441323eb 100644 --- a/apps/design-system/content/docs/color-usage.mdx +++ b/apps/design-system/content/docs/color-usage.mdx @@ -75,4 +75,6 @@ These can also be accessed with `foreground`. Like `text-foreground-light`. Every Radix scale exposed via `--colors-{name}{1..12}`. Click a swatch to copy the CSS variable reference. The colors are taken from `@radix-ui/colors` v0.1.9 except the `Brand` and `Scale` colors. +The `Brand` colors issue of not being uniform across the whole scale is known, they need to be refactored. The `brand-200` variable in the dark themes is also bad. + diff --git a/apps/studio/components/interfaces/Billing/Payment/PaymentMethods/CreditCard.tsx b/apps/studio/components/interfaces/Billing/Payment/PaymentMethods/CreditCard.tsx index 13638070517..4c9b0429188 100644 --- a/apps/studio/components/interfaces/Billing/Payment/PaymentMethods/CreditCard.tsx +++ b/apps/studio/components/interfaces/Billing/Payment/PaymentMethods/CreditCard.tsx @@ -125,7 +125,7 @@ const CreditCard = ({

{isSpt && spt && ( -
+

Managed via Stripe Projects ยท Token ending in{' '} {spt.last4} diff --git a/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx b/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx index 07e707155f4..82ca9c7a8da 100644 --- a/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx +++ b/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx @@ -175,7 +175,7 @@ export const ConnectionPanel = ({ onCopyCallback={onCopyCallback} /> {notice && ( -

+
{notice?.map((text: string) => (

{text} diff --git a/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx b/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx index 4b3348b5e41..7290919f6a0 100644 --- a/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx +++ b/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx @@ -27,7 +27,7 @@ export const ConnectionParameters = ({ parameters }: ConnectionParametersProps)

๐Ÿค”

"); +} diff --git a/packages/config/css/variants.css b/packages/config/css/variants.css new file mode 100644 index 00000000000..db88749e830 --- /dev/null +++ b/packages/config/css/variants.css @@ -0,0 +1,30 @@ +/* + * Custom Tailwind v4 variants migrated from: + * - packages/config/ui.config.js addVariant calls + * - packages/config/tailwind.config.js addVariant + darkMode setting + */ + +/* + * `darkMode: ['class', '[data-theme*="dark"]']` from the v3 config is replaced + * by this @custom-variant. The :where() wrapper keeps specificity at 0 so + * the dark utilities don't override unrelated rules. + */ +@custom-variant dark (&:where([data-theme*='dark'] *, [data-theme*='dark'])); + +@custom-variant data-open-parent ([data-state='open'] &); +@custom-variant data-closed-parent ([data-state='closed'] &); +@custom-variant data-open (&[data-state='open']); +@custom-variant data-closed (&[data-state='closed']); + +/* may be unused โ€” verify before pruning */ +@custom-variant data-show (&[data-state='show']); +/* may be unused โ€” verify before pruning */ +@custom-variant data-hide (&[data-state='hide']); + +@custom-variant data-checked (&[data-state='checked']); +/* may be unused โ€” verify before pruning */ +@custom-variant data-unchecked (&[data-state='unchecked']); + +@custom-variant aria-expanded (&[aria-expanded='true']); + +@custom-variant not-disabled (&:not(:disabled)); diff --git a/packages/config/default-colors.js b/packages/config/default-colors.js deleted file mode 100644 index 3373e649d3b..00000000000 --- a/packages/config/default-colors.js +++ /dev/null @@ -1,90 +0,0 @@ -module.exports = { - brand: { - brand1: 'hsla(153, 80%, 99%, 1)', - brand2: 'hsla(148, 88%, 97%, 1)', - brand3: 'hsla(148, 76%, 95%, 1)', - brand4: 'hsla(148, 68%, 92%, 1)', - brand5: 'hsla(148, 59%, 88%, 1)', - brand6: 'hsla(149, 43%, 82%, 1)', - brand7: 'hsla(149, 43%, 69%, 1)', - brand8: 'hsla(154, 55%, 45%, 1)', - brand9: 'hsla(153, 60%, 53%, 1)', - brand10: 'hsla(153, 50%, 50%, 1)', - brand11: 'hsla(153, 50%, 34%, 1)', - brand12: 'hsla(153, 40%, 12%, 1)', - }, - brandDark: { - brand1: 'hsla(153, 75%, 6%, 1)', - brand2: 'hsla(153, 73%, 7%, 1)', - brand3: 'hsla(154, 69%, 9%, 1)', - brand4: 'hsla(154, 67%, 11%, 1)', - brand5: 'hsla(154, 66%, 13%, 1)', - brand6: 'hsla(154, 64%, 17%, 1)', - brand7: 'hsla(154, 62%, 22%, 1)', - brand8: 'hsla(153, 60%, 28%, 1)', - brand9: 'hsla(153, 60%, 53%, 1)', - brand10: 'hsla(153, 60%, 70%, 1)', - brand11: 'hsla(153, 60%, 50%, 1)', - brand12: 'hsla(153, 60%, 95%, 1)', - }, - contrast: { - 'brand-hiContrast': 'hsl(var(--brand-default))', - 'brand-loContrast': 'hsl(var(--brand-300))', - }, - scale: { - scale1: 'var(--colors-slate1)', - scale2: 'var(--colors-slate2)', - scale3: 'var(--colors-slate3)', - scale4: 'var(--colors-slate4)', - scale5: 'var(--colors-slate5)', - scale6: 'var(--colors-slate6)', - scale7: 'var(--colors-slate7)', - scale8: 'var(--colors-slate8)', - scale9: 'var(--colors-slate9)', - scale10: 'var(--colors-slate10)', - scale11: 'var(--colors-slate11)', - scale12: 'var(--colors-slate12)', - }, - scaleA: { - scaleA1: 'var(--colors-slateA1)', - scaleA2: 'var(--colors-slateA2)', - scaleA3: 'var(--colors-slateA3)', - scaleA4: 'var(--colors-slateA4)', - scaleA5: 'var(--colors-slateA5)', - scaleA6: 'var(--colors-slateA6)', - scaleA7: 'var(--colors-slateA7)', - scaleA8: 'var(--colors-slateA8)', - scaleA9: 'var(--colors-slateA9)', - scaleA10: 'var(--colors-slateA10)', - scaleA11: 'var(--colors-slateA11)', - scaleA12: 'var(--colors-slateA12)', - }, - scaleDark: { - scale1: 'var(--colors-gray1)', - scale2: 'var(--colors-gray2)', - scale3: 'var(--colors-gray3)', - scale4: 'var(--colors-gray4)', - scale5: 'var(--colors-gray5)', - scale6: 'var(--colors-gray6)', - scale7: 'var(--colors-gray7)', - scale8: 'var(--colors-gray8)', - scale9: 'var(--colors-gray9)', - scale10: 'var(--colors-gray10)', - scale11: '#bbbbbb', - scale12: 'var(--colors-gray12)', - }, - scaleADark: { - scaleA1: 'var(--colors-grayA1)', - scaleA2: 'var(--colors-grayA2)', - scaleA3: 'var(--colors-grayA3)', - scaleA4: 'var(--colors-grayA4)', - scaleA5: 'var(--colors-grayA5)', - scaleA6: 'var(--colors-grayA6)', - scaleA7: 'var(--colors-grayA7)', - scaleA8: 'var(--colors-grayA8)', - scaleA9: 'var(--colors-grayA9)', - scaleA10: 'var(--colors-grayA10)', - scaleA11: 'var(--colors-grayA11)', - scaleA12: 'var(--colors-grayA12)', - }, -} diff --git a/packages/config/package.json b/packages/config/package.json index 5ce20f9026a..87b3bc63796 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -8,15 +8,11 @@ "clean": "rimraf node_modules" }, "dependencies": { - "@mertasan/tailwindcss-variables": "^2.2.3", - "@radix-ui/colors": "^0.1.8", "@tailwindcss/forms": "^0.5.11", - "@tailwindcss/typography": "^0.5.19", - "deepmerge": "^4.2.2", - "mini-svg-data-uri": "^1.4.3", - "tailwindcss-radix": "^2.0.0" + "@tailwindcss/typography": "^0.5.19" }, "devDependencies": { + "@radix-ui/colors": "^0.1.8", "@tailwindcss/postcss": "^4.2.4", "tailwindcss": "catalog:", "tailwindcss-animate": "^1.0.7", diff --git a/packages/config/scripts/generate-colors.js b/packages/config/scripts/generate-colors.js new file mode 100644 index 00000000000..f6f7ba43387 --- /dev/null +++ b/packages/config/scripts/generate-colors.js @@ -0,0 +1,251 @@ +#!/usr/bin/env node +/** + * Emits packages/config/css/colors.css and packages/config/css/theme.css + * from packages/config/scripts/tw-extend-color-snapshot.js. Run from + * packages/config/. + * + * - colors.css holds a :root block that declares the Radix/scale --color-* + * tokens, forwarding each one to its underlying --colors-{hue}{n} source + * var (which switches across light/dark themes via radix-vars.css). This + * is where the actual runtime value lives. Brand steps 200..600 forward + * to the semantic hsl(var(--brand-{step})) triplet instead of the raw + * --colors-brand{n} scale value. + * - theme.css holds the @theme inline block of --color-* design tokens + * that Tailwind v4 uses to generate bg-{name}, text-{name}, border-{name}, + * ring-{name}, plus directional border variants like border-r-{name}. + * Semantic tokens wrap their source HSL triplet in hsl(); Radix/scale + * tokens self-reference the matching var declared in :root (colors.css) + * so theme switching works at runtime without re-registering utilities. + * The file also contains hand-curated namespaced utility blocks + * (--background-color-*, --border-color-*, --text-color-*) that only + * generate utilities for a single Tailwind namespace, plus a deprecated + * --color-border-* block. + */ + +const fs = require('node:fs') +const path = require('node:path') + +// Snapshot of the v3 tw-extend/color.js (was deleted alongside the migration). +// Kept here as a generation source so the script remains runnable. +const colorEntries = require('./tw-extend-color-snapshot.js') + +const themeLines = [] +const rootLines = [] + +// Emit prefixed --color-* tokens for every semantic entry in the snapshot. +// `foo-DEFAULT` becomes `--color-foo`; everything else keeps its full key. +// `border-*` entries are intentionally skipped here โ€” they live in the +// deprecated --color-border-* block in `namespacedTokens` below, which is +// curated by hand (the snapshot has more border entries than we want to +// expose as the legacy `--color-border-*` namespace). +for (const [key, { cssVariable }] of Object.entries(colorEntries)) { + if (/var\(--core-/.test(cssVariable)) continue + if (key.startsWith('border-')) continue + const themeKey = key.replace(/-DEFAULT$/, '') + themeLines.push(` --color-${themeKey}: hsl(${cssVariable});`) +} + +// --- Hand-rolled additions that don't come from the snapshot --- + +// Radix hue palettes โ€” each --color-{hue}-{step} resolves to the raw hue var. +const radixHues = [ + 'amber', + 'blue', + 'crimson', + 'gold', + 'gray', + 'green', + 'indigo', + 'orange', + 'pink', + 'purple', + 'red', + 'slate', + 'tomato', + 'violet', + 'yellow', +] +for (const hue of radixHues) { + for (let i = 1; i <= 12; i++) { + const solid = `--color-${hue}-${i * 100}` + const alpha = `--color-${hue}A-${i * 100}` + rootLines.push(` ${solid}: var(--colors-${hue}${i});`) + rootLines.push(` ${alpha}: var(--colors-${hue}A${i});`) + themeLines.push(` ${solid}: var(${solid});`) + themeLines.push(` ${alpha}: var(${alpha});`) + } +} + +// blackA / whiteA โ€” neutral alpha scales. +for (let i = 1; i <= 12; i++) { + const black = `--color-blackA-${i * 100}` + const white = `--color-whiteA-${i * 100}` + rootLines.push(` ${black}: var(--colors-blackA${i});`) + rootLines.push(` ${white}: var(--colors-whiteA${i});`) + themeLines.push(` ${black}: var(${black});`) + themeLines.push(` ${white}: var(${white});`) +} + +// Supabase scale + scaleA. Underlying --colors-scale{n} / --colors-scaleA{n} +// are aliases (defined in radix-vars.css) that switch between slate/gray +// across light/dark themes. +for (let i = 1; i <= 12; i++) { + const solid = `--color-scale-${i * 100}` + const alpha = `--color-scaleA-${i * 100}` + rootLines.push(` ${solid}: var(--colors-scale${i});`) + rootLines.push(` ${alpha}: var(--colors-scaleA${i});`) + themeLines.push(` ${solid}: var(${solid});`) + themeLines.push(` ${alpha}: var(${alpha});`) +} + +// Supabase brand scale. Steps 200..600 are semantic: they forward to +// hsl(var(--brand-{step})) so light/dark themes can override the value. +// The other steps fall back to the raw --colors-brand{n} scale and get +// a self-referencing entry in theme.css; the semantic steps are already +// emitted to themeLines by the snapshot pass above (brand-200..600, +// brand-DEFAULT, brand-link), so we don't re-emit them here. +const SEMANTIC_BRAND_STEPS = new Set([2, 3, 4, 5, 6]) +for (let i = 1; i <= 12; i++) { + const solid = `--color-brand-${i * 100}` + if (SEMANTIC_BRAND_STEPS.has(i)) { + rootLines.push(` ${solid}: hsl(var(--brand-${i * 100}));`) + } else { + rootLines.push(` ${solid}: var(--colors-brand${i});`) + themeLines.push(` ${solid}: var(${solid});`) + } +} + +// Sidebar shadcn semantic tokens. +themeLines.push(` --color-sidebar: hsl(var(--sidebar-background));`) +themeLines.push(` --color-sidebar-foreground: hsl(var(--sidebar-foreground));`) +themeLines.push(` --color-sidebar-primary: hsl(var(--sidebar-primary));`) +themeLines.push(` --color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));`) +themeLines.push(` --color-sidebar-accent: hsl(var(--sidebar-accent));`) +themeLines.push(` --color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));`) +themeLines.push(` --color-sidebar-border: hsl(var(--sidebar-border));`) +themeLines.push(` --color-sidebar-ring: hsl(var(--sidebar-ring));`) + +// Misc semantic colors. +themeLines.push(` --color-hi-contrast: hsl(var(--foreground-default));`) +themeLines.push(` --color-lo-contrast: hsl(var(--background-alternative-default));`) + +// Namespaced utility blocks โ€” these intentionally use Tailwind v4's per- +// namespace --{property}-color-* keys so the values only generate utilities +// for a single namespace (bg-, border-, text-) rather than all of them. +const namespacedTokens = ` /* + * This will generate colors only for bg utilities. + */ + --background-color-200: hsl(var(--background-200)); + --background-color-default: hsl(var(--background-default)); + --background-color-muted: hsl(var(--background-muted)); + --background-color-alternative-200: hsl(var(--background-alternative-200)); + --background-color-alternative: hsl(var(--background-alternative-default)); + --background-color-selection: hsl(var(--background-selection)); + --background-color-control: hsl(var(--background-control)); + --background-color-surface-75: hsl(var(--background-surface-75)); + --background-color-surface-100: hsl(var(--background-surface-100)); + --background-color-surface-200: hsl(var(--background-surface-200)); + --background-color-surface-300: hsl(var(--background-surface-300)); + --background-color-surface-400: hsl(var(--background-surface-400)); + --background-color-overlay: hsl(var(--background-overlay-default)); + --background-color-overlay-hover: hsl(var(--background-overlay-hover)); + --background-color-button: hsl(var(--background-button-default)); + --background-color-dialog: hsl(var(--background-dialog-default)); + --background-color-dash-sidebar: hsl(var(--background-dash-sidebar)); + --background-color-dash-canvas: hsl(var(--background-dash-canvas)); + --background-color-studio: hsl(var(--background-200)); + + /* + * This will generate colors for all utilities, they'll have border in the name i.e. bg-border-overlay, text-border-muted + * This usage should be discouraged and deprecated. + */ + --color-border: hsl(var(--border-default)); + --color-border-overlay: hsl(var(--border-overlay)); + --color-border-muted: hsl(var(--border-muted)); + --color-border-control: hsl(var(--border-control)); + --color-border-strong: hsl(var(--border-strong)); + --color-border-stronger: hsl(var(--border-stronger)); + /* + * This will generate colors only for border utilities + * i.e. bg-muted and they'll only have border-color: var(--border-muted) rule + */ + --border-color-default: hsl(var(--border-default)); + --border-color-overlay: hsl(var(--border-overlay)); + --border-color-muted: hsl(var(--border-muted)); + --border-color-control: hsl(var(--border-control)); + --border-color-strong: hsl(var(--border-strong)); + --border-color-stronger: hsl(var(--border-stronger)); + --border-color-secondary: hsl(var(--border-secondary)); + --border-color-alternative: hsl(var(--border-alternative)); + --border-color-button: hsl(var(--border-button-default)); + --border-color-button-hover: hsl(var(--border-button-hover)); + + /* This will generate colors only for text utilities */ + --text-color-default: hsl(var(--foreground-default)); + --text-color-light: hsl(var(--foreground-light)); + --text-color-lighter: hsl(var(--foreground-lighter)); + --text-color-muted: hsl(var(--foreground-muted)); + --text-color-contrast: hsl(var(--foreground-contrast));` + +// Misc design tokens (font, breakpoint, sizing, radius, transform-origin) +// that lived in tailwind.config.js / ui.config.js theme blocks. +const miscTokens = ` /* misc theme tokens โ€” typography, breakpoints, sizing, transform-origin */ + --font-sans: var( + --font-custom, + Circular, + custom-font, + Helvetica Neue, + Helvetica, + Arial, + sans-serif + ); + --font-mono: var(--font-source-code-pro, Source Code Pro, Office Code Pro, Menlo, monospace); + --breakpoint-xs: 480px; + --width-listbox: 320px; + --radius-panel: 6px; + --spacing-content: 21px; + --spacing-card: var(--card-padding-x); + --transform-origin-dropdown: var(--radix-dropdown-menu-content-transform-origin); + --transform-origin-popover: var(--radix-popover-menu-content-transform-origin);` + +const colorsCss = `/* + * Auto-generated by scripts/generate-colors.js โ€” do not edit by hand. + * Run \`node scripts/generate-colors.js\` from packages/config/ to refresh. + * + * Declares the Radix/scale --color-* CSS variables in :root, each forwarding + * to its underlying --colors-{hue}{n} source (defined in radix-vars.css) + * which switches between light/dark themes. theme.css references these + * variables from its @theme inline block. + */ + +:root { +${rootLines.join('\n')} +} +` + +const themeCss = `/* + * Auto-generated by scripts/generate-colors.js โ€” do not edit by hand. + * Run \`node scripts/generate-colors.js\` from packages/config/ to refresh. + * + * Defines every Tailwind v4 --color-* design token. Tailwind generates + * bg-{name} / text-{name} / border-{name} / ring-{name} / border-{r,l,t,b}-{name} + * utilities for each entry. Semantic tokens wrap their HSL triplet in hsl(); + * Radix/scale tokens self-reference the var declared in colors.css :root. + * Source mappings: tw-extend-color-snapshot.js (semantic tokens) plus the + * Radix and scale aliases from radix-vars.css. + */ + +@theme inline { +${themeLines.join('\n')} + +${namespacedTokens} + +${miscTokens} +} +` + +const outDir = path.resolve(__dirname, '..', 'css') +fs.writeFileSync(path.join(outDir, 'colors.css'), colorsCss) +fs.writeFileSync(path.join(outDir, 'theme.css'), themeCss) +console.log(`wrote ${path.join(outDir, 'colors.css')} (${colorsCss.split('\n').length} lines)`) +console.log(`wrote ${path.join(outDir, 'theme.css')} (${themeCss.split('\n').length} lines)`) diff --git a/packages/config/scripts/generate-radix-vars.js b/packages/config/scripts/generate-radix-vars.js new file mode 100644 index 00000000000..69a4a1a677a --- /dev/null +++ b/packages/config/scripts/generate-radix-vars.js @@ -0,0 +1,84 @@ +#!/usr/bin/env node +/** + * Emits packages/config/css/radix-vars.css from @radix-ui/colors and the + * existing default-colors.js scale aliases. Run from packages/config/. + * + * One-shot generator โ€” committed alongside the CSS output so future updates + * (new Radix versions, new aliases) can be reproduced without searching for + * how the file was made. + */ + +const fs = require('node:fs') +const path = require('node:path') + +const radixUiColors = require('@radix-ui/colors') + +const excludedRadixColors = [ + 'bronze', + 'brown', + 'cyan', + 'grass', + 'olive', + 'mauve', + 'mint', + 'lime', + 'plum', + 'sage', + 'sand', + 'sky', + 'teal', +] + +const isExcluded = (key) => excludedRadixColors.some((ex) => key.startsWith(ex)) + +const lightEntries = [] +const darkEntries = [] + +for (const [key, palette] of Object.entries(radixUiColors)) { + if (isExcluded(key)) continue + const target = key.includes('Dark') ? darkEntries : lightEntries + for (const [varName, value] of Object.entries(palette)) { + target.push([varName, value]) + } +} + +const formatBlock = (selector, entries) => { + const lines = entries.map(([k, v]) => ` --colors-${k}: ${v};`).join('\n') + return `${selector} {\n${lines}\n}` +} + +const scaleAliasesLight = [] +const scaleAliasesDark = [] +for (let i = 1; i <= 12; i++) { + scaleAliasesLight.push([`scale${i}`, `var(--colors-slate${i})`]) + scaleAliasesLight.push([`scaleA${i}`, `var(--colors-slateA${i})`]) + if (i === 11) { + // Preserves the hard-coded value from default-colors.js scaleDark.scale11. + scaleAliasesDark.push([`scale${i}`, `#bbbbbb`]) + } else { + scaleAliasesDark.push([`scale${i}`, `var(--colors-gray${i})`]) + } + scaleAliasesDark.push([`scaleA${i}`, `var(--colors-grayA${i})`]) +} + +const header = `/* + * Auto-generated by scripts/generate-radix-vars.js โ€” do not edit by hand. + * Run \`node scripts/generate-radix-vars.js\` from packages/config/ to refresh. + * + * Mirrors the CSS variables that the legacy @mertasan/tailwindcss-variables + * plugin used to emit from ui.config.js's generateCssVariables() and + * default-colors.js's scale/scaleA aliases. + */ +` + +const output = [ + header, + formatBlock(':root', [...lightEntries, ...scaleAliasesLight]), + '', + formatBlock("[data-theme*='dark']", [...darkEntries, ...scaleAliasesDark]), + '', +].join('\n') + +const outPath = path.resolve(__dirname, '..', 'css', 'radix-vars.css') +fs.writeFileSync(outPath, output) +console.log(`wrote ${outPath} (${output.split('\n').length} lines)`) diff --git a/packages/ui/build/css/tw-extend/color.js b/packages/config/scripts/tw-extend-color-snapshot.js similarity index 100% rename from packages/ui/build/css/tw-extend/color.js rename to packages/config/scripts/tw-extend-color-snapshot.js diff --git a/packages/config/tailwind.config.css b/packages/config/tailwind.config.css index e773125bea8..2a6e4546c0d 100644 --- a/packages/config/tailwind.config.css +++ b/packages/config/tailwind.config.css @@ -1,6 +1,41 @@ @import 'tailwindcss'; + +/* JS plugins. Tailwind v4 supports `@plugin` for both v3-style JS plugins and + * v4-native packages. The two local plugins use theme('spacing') / + * theme('transitionDuration') which resolve from v4's @theme tokens. */ +@plugin "@tailwindcss/typography"; +@plugin "@tailwindcss/forms"; +@plugin "tailwindcss-animate"; +@plugin "./tailwind-plugins/hit-area"; +@plugin "./tailwind-plugins/motion-safe-transition"; + +/* Raw color values + per-theme overrides. Must come before colors.css so the + * --colors-* references resolve to a value. */ +@import './css/radix-vars.css'; +@import './css/brand-vars.css'; @import '../ui/build/css/source/global.css'; @import '../ui/build/css/themes/dark.css'; @import '../ui/build/css/themes/light.css'; + +/* Reset Tailwind's built-in color tokens before re-defining our own, so e.g. + * --color-amber-* doesn't ship a default Tailwind value alongside the Radix + * one. */ @import './unset-tw-colors.css'; -@config './tailwind.config.js'; + +/* Theme tokens + utilities + variants + base layer. colors.css declares the + * Radix/scale CSS variables in :root; theme.css consumes them from + * @theme inline so Tailwind generates the utilities. */ +@import './css/colors.css'; +@import './css/theme.css'; +@import './css/animations.css'; +@import './css/utilities.css'; +@import './css/variants.css'; +@import './css/base.css'; + +/* + * Typography prose / prose-toc / prose-docs overrides live in a focused JS + * config because @tailwindcss/typography reads its theme.typography options + * from the JS config โ€” not from CSS @theme tokens. See: + * https://github.com/tailwindlabs/tailwindcss-typography#customizing-the-css + */ +@config './typography.config.js'; diff --git a/packages/config/tailwind.config.js b/packages/config/tailwind.config.js deleted file mode 100644 index a32b36e179a..00000000000 --- a/packages/config/tailwind.config.js +++ /dev/null @@ -1,449 +0,0 @@ -const ui = require('./ui.config.js') -const deepMerge = require('deepmerge') -const plugin = require('tailwindcss/plugin') - -const color = require('./../ui/build/css/tw-extend/color') - -/** - * - */ -let colorExtend = {} -Object.values(color).map((x, i) => { - colorExtend[Object.keys(color)[i]] = `hsl(${x.cssVariable})` // x.cssVariable -}) - -// console.log('colorExtend', colorExtend) -// console.log('colorExtend kebabToNested', kebabToNested(colorExtend)) - -// console.log('colorExtend', kebabToNested(colorExtend).colors.gray) - -/** - * Generates Tailwind colors for the theme - */ -function generateTwColorClasses(globalKey, twAttributes) { - let classes = {} - Object.values(twAttributes).map((attr, i) => { - const attrKey = Object.keys(twAttributes)[i] - - if (attrKey.includes(globalKey)) { - const keySplit = attrKey.split('-').splice(1).join('-') - - let payload = { - [keySplit]: `hsl(${attr.cssVariable})`, - } - - if (keySplit == 'DEFAULT') { - // includes a 'default' duplicate - // this allows for classes like `border-default` which is the same as `border` - payload = { - ...payload, - default: `hsl(${attr.cssVariable})`, - } - } - - classes = { - ...classes, - ...payload, - } - } - }) - /** - * mutate object into nested object for tailwind theme structure - */ - const nestedClasses = kebabToNested(classes) - // return, but nest the keys if they are kebab case named - return nestedClasses -} - -/** - * Helper to convert kebab named keys in object to nested nodes - */ -function kebabToNested(obj) { - const result = {} - for (const [key, value] of Object.entries(obj)) { - const parts = key.split('-') - let currentObj = result - for (let i = 0; i < parts.length; i++) { - const part = parts[i] === 'DEFAULT' ? parts[i] : parts[i].toLowerCase() // convert key to lowercase - if (!currentObj[part]) { - currentObj[part] = {} - } - if (i === parts.length - 1) { - if (typeof value === 'object') { - currentObj[part] = kebabToNested(value) // recursively convert nested objects - } else { - currentObj[part] = value.toString().toLowerCase() // convert value to lowercase - } - } else { - currentObj = currentObj[part] - } - } - } - return result -} - -/** - * Main theme config - */ -const uiConfig = ui({ - darkMode: ['class', '[data-theme*="dark"]'], - theme: { - /** - * Spread all theme colors and custom generated colors into theme - */ - textColor: (theme) => ({ - ...theme('colors'), - ...generateTwColorClasses('foreground', color), - }), - backgroundColor: (theme) => ({ - ...theme('colors'), - ...generateTwColorClasses('background', color), - /* - * custom background re-maps - */ - studio: `hsl(var(--background-200))`, - }), - borderColor: (theme) => ({ - ...theme('colors'), - ...generateTwColorClasses('border', color), - }), - extend: { - colors: { - ...kebabToNested(colorExtend), - sidebar: { - DEFAULT: 'hsl(var(--sidebar-background))', - foreground: 'hsl(var(--sidebar-foreground))', - primary: 'hsl(var(--sidebar-primary))', - 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', - accent: 'hsl(var(--sidebar-accent))', - 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', - border: 'hsl(var(--sidebar-border))', - ring: 'hsl(var(--sidebar-ring))', - }, - }, - - typography: ({ theme }) => ({ - // Removal of backticks in code blocks for tailwind v3.0 - // https://github.com/tailwindlabs/tailwindcss-typography/issues/135 - DEFAULT: { - css: { - 'code::before': { - content: '""', - }, - 'code::after': { - content: '""', - }, - '--tw-prose-body': 'hsl(var(--foreground-light))', - '--tw-prose-headings': 'hsl(var(--foreground-default))', - '--tw-prose-lead': 'hsl(var(--foreground-light))', - '--tw-prose-links': 'hsl(var(--foreground-light))', - '--tw-prose-bold': 'hsl(var(--foreground-light))', - '--tw-prose-counters': 'hsl(var(--foreground-light))', - '--tw-prose-bullets': 'hsl(var(--foreground-muted))', - '--tw-prose-hr': 'hsl(var(--background-surface-300))', - '--tw-prose-quotes': 'hsl(var(--foreground-light))', - '--tw-prose-quote-borders': 'hsl(var(--background-surface-300))', - '--tw-prose-captions': 'hsl(var(--border-strong))', - '--tw-prose-code': 'hsl(var(--foreground-default))', - '--tw-prose-pre-code': 'hsl(var(--foreground-muted))', - '--tw-prose-pre-bg': 'hsl(var(--background-surface-200))', - '--tw-prose-th-borders': 'hsl(var(--background-surface-300))', - '--tw-prose-td-borders': 'hsl(var(--background-default))', - '--tw-prose-invert-body': 'hsl(var(--background-default))', - '--tw-prose-invert-headings': theme('colors.white'), - '--tw-prose-invert-lead': 'hsl(var(--background-surface-300))', - '--tw-prose-invert-links': theme('colors.white'), - '--tw-prose-invert-bold': theme('colors.white'), - '--tw-prose-invert-counters': 'hsl(var(--background-surface-200))', - '--tw-prose-invert-bullets': 'hsl(var(--background-selection))', - '--tw-prose-invert-hr': 'hsl(var(--border-strong))', - '--tw-prose-invert-quotes': 'hsl(var(--background-alternative-default))', - '--tw-prose-invert-quote-borders': 'hsl(var(--border-strong))', - '--tw-prose-invert-captions': 'hsl(var(--background-surface-200))', - // the following are typography overrides - // examples can be seen here โ€”> https://github.com/tailwindlabs/tailwindcss-typography/blob/master/src/styles.js - // reset all header font weights - h4: { - // override font size - fontSize: '1.15em', - }, - h5: { - // h5 not included in --tw-prose-headings - color: theme('colors.scale[1200]'), - }, - 'h1, h2, h3, h4, h5, h6': { - fontWeight: '400', - }, - 'article h2, article h3, article h4, article h5, article h6': { - marginTop: '2em', - marginBottom: '1em', - }, - p: { - fontWeight: '400', - }, - strong: { - fontWeight: '500', - }, - pre: { - background: 'none', - padding: 0, - marginBottom: '32px', - }, - ul: { - listStyleType: 'none', - paddingLeft: '1rem', - }, - 'ul li': { - position: 'relative', - }, - 'ul li::before': { - position: 'absolute', - top: '0.75rem', - left: '-1rem', - height: '0.125rem', - width: '0.5rem', - borderRadius: '0.25rem', - backgroundColor: 'hsl(var(--border-strong))', - content: '""', - }, - ol: { - paddingLeft: '1rem', - counterReset: 'item', - listStyleType: 'none', - marginBottom: '3rem', - }, - 'ol>li': { - display: 'block', - position: 'relative', - paddingLeft: '1rem', - }, - 'ol>li::before': { - position: 'absolute', - top: '0.25rem', - left: '-1rem', - height: '1.2rem', - width: '1.2rem', - borderRadius: '0.25rem', - backgroundColor: 'hsl(var(--background-surface-100))', - border: '1px solid hsl(var(--border-default))', - content: 'counter(item) " "', - counterIncrement: 'item', - fontSize: '12px', - color: 'hsl(var(--foreground-muted))', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - - 'p img': { - border: '1px solid hsl(var(--border-muted))', - borderRadius: '4px', - overflow: 'hidden', - }, - iframe: { - border: '1px solid ' + theme('borderColor.DEFAULT'), - borderRadius: theme('borderRadius.lg'), - }, - td: { - borderBottom: '1px solid ' + 'hsl(var(--background-surface-200))', - }, - code: { - fontWeight: '400', - padding: '0.2rem 0.4rem', - backgroundColor: 'hsl(var(--background-surface-200))', - border: '1px solid ' + 'hsl(var(--background-surface-300))', - borderRadius: theme('borderRadius.lg'), - }, - a: { - position: 'relative', - transition: 'all 0.18s ease', - paddingBottom: '2px', - fontWeight: '400', - opacity: 1, - color: 'hsl(var(--foreground-default))', - textDecorationLine: 'underline', - textDecorationColor: 'hsl(var(--foreground-muted))', - textDecorationThickness: '1px', - textUnderlineOffset: '2px', - }, - 'a:hover': { - textDecorationColor: 'hsl(var(--foreground-default))', - }, - figcaption: { - color: 'hsl(var(--foreground-muted))', - fontFamily: 'Office Code Pro, monospace', - }, - 'figure.quote-figure p:first-child': { - marginTop: '0 !important', - }, - 'figure.quote-figure p:last-child': { - marginBottom: '0 !important', - }, - figure: { - margin: '3rem 0', - }, - 'figure img': { - margin: '0 !important', - }, - }, - }, - - toc: { - css: { - ul: { - 'list-style-type': 'none', - 'padding-left': 0, - margin: 0, - li: { - 'padding-left': 0, - }, - a: { - display: 'block', - marginBottom: '0.4rem', - 'text-decoration': 'none', - fontSize: '0.8rem', - fontWeight: '200', - color: 'hsl(var(--foreground-light))', - '&:hover': { - color: 'hsl(var(--foreground-default))', - }, - 'font-weight': '400', - }, - // margin: 0, - ul: { - 'list-style-type': 'none', - li: { - marginTop: '0.2rem', - marginBottom: '0.2rem', - 'padding-left': '0 !important', - 'margin-left': '0.5rem', - }, - a: { - fontWeight: '200', - color: 'hsl(var(--foreground-lighter))', - '&:hover': { - color: 'hsl(var(--foreground-default))', - }, - }, - }, - }, - }, - }, - // used in auto docs - docs: { - css: { - '--tw-prose-body': 'hsl(var(--foreground-light))', - '--tw-prose-headings': 'hsl(var(--foreground-default))', - '--tw-prose-lead': 'hsl(var(--foreground-light))', - '--tw-prose-links': 'hsl(var(--brand-500))', - '--tw-prose-bold': 'hsl(var(--foreground-light))', - '--tw-prose-counters': 'hsl(var(--foreground-light))', - '--tw-prose-bullets': 'hsl(var(--foreground-muted))', - '--tw-prose-hr': 'hsl(var(--background-surface-300))', - '--tw-prose-quotes': 'hsl(var(--foreground-light))', - '--tw-prose-quote-borders': 'hsl(var(--background-surface-300))', - '--tw-prose-captions': 'hsl(var(--border-strong))', - '--tw-prose-code': 'hsl(var(--foreground-default))', - '--tw-prose-pre-code': 'hsl(var(--foreground-muted))', - '--tw-prose-pre-bg': 'hsl(var(--background-surface-200))', - '--tw-prose-th-borders': 'hsl(var(--background-surface-300))', - '--tw-prose-td-borders': 'hsl(var(--background-default))', - '--tw-prose-invert-body': 'hsl(var(--background-default))', - '--tw-prose-invert-headings': theme('colors.white'), - '--tw-prose-invert-lead': 'hsl(var(--background-surface-300))', - '--tw-prose-invert-links': theme('colors.white'), - '--tw-prose-invert-bold': theme('colors.white'), - '--tw-prose-invert-counters': 'hsl(var(--background-surface-200))', - '--tw-prose-invert-bullets': 'hsl(var(--background-selection))', - '--tw-prose-invert-hr': 'hsl(var(--border-strong))', - '--tw-prose-invert-quotes': 'hsl(var(--background-alternative-default))', - '--tw-prose-invert-quote-borders': 'hsl(var(--border-strong))', - '--tw-prose-invert-captions': 'hsl(var(--background-surface-200))', - // the following are typography overrides - // examples can be seen here โ€”> https://github.com/tailwindlabs/tailwindcss-typography/blob/master/src/styles.js - // reset all header font weights - 'h1, h2, h3, h4, h5': { - fontWeight: '400', - }, - }, - }, - }), - screens: { - xs: '480px', - }, - fontFamily: { - sans: 'var(--font-custom, Circular, custom-font, Helvetica Neue, Helvetica, Arial, sans-serif)', - mono: 'var(--font-source-code-pro, Source Code Pro, Office Code Pro, Menlo, monospace)', - }, - - // shadcn defaults START - keyframes: { - 'flash-code': { - '0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' }, - '100%': { backgroundColor: 'transparent' }, - }, - 'accordion-down': { - from: { height: 0 }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: 0 }, - }, - 'collapsible-down': { - from: { height: 0 }, - to: { height: 'var(--radix-collapsible-content-height)' }, - }, - 'collapsible-up': { - from: { height: 'var(--radix-collapsible-content-height)' }, - to: { height: 0 }, - }, - }, - animation: { - 'flash-code': 'flash-code 1s forwards', - 'flash-code-slow': 'flash-code 2s forwards', - 'accordion-down': 'accordion-down 0.15s ease-out', - 'accordion-up': 'accordion-up 0.15s ease-out', - 'collapsible-down': 'collapsible-down 0.10s ease-out', - 'collapsible-up': 'collapsible-up 0.10s ease-out', - }, - borderRadius: { - // lg: `var(--radius)`, - // md: `calc(var(--radius) - 2px)`, - // sm: 'calc(var(--radius) - 4px)', - panel: '6px', - }, - padding: { - content: '21px', - card: 'var(--card-padding-x)', - }, - // borderRadius: { - // lg: `var(--radius)`, - // md: `calc(var(--radius) - 2px)`, - // sm: 'calc(var(--radius) - 4px)', - // }, - // fontFamily: { - // sans: ['var(--font-sans)', ...fontFamily.sans], - // }, - // shadcn defaults END - }, - }, - plugins: [ - require('@tailwindcss/typography'), - require('tailwindcss-animate'), - require('./tailwind-plugins/hit-area'), - require('./tailwind-plugins/motion-safe-transition'), - function ({ addVariant }) { - addVariant('not-disabled', '&:not(:disabled)') - }, - function ({ addUtilities }) { - // The bg class worked in Tailwind 3, but stopped working in TW 4. This is a custom utility to add it back in, as - // it's used in a lot of places across the codebase. We should deprecate it and use `bg-default` instead. - addUtilities({ - '.bg': { backgroundColor: 'hsl(var(--background-default))' }, - }) - }, - ], -}) - -module.exports = uiConfig diff --git a/packages/config/typography.config.js b/packages/config/typography.config.js new file mode 100644 index 00000000000..69efdd74ae2 --- /dev/null +++ b/packages/config/typography.config.js @@ -0,0 +1,232 @@ +/** + * Typography customization for @tailwindcss/typography. + * + * Loaded via `@config './typography.config.js'` in tailwind.config.css. + * Only contains theme.extend.typography โ€” everything else (colors, animations, + * variants, utilities, base) lives in CSS under packages/config/css/. + * + * The typography plugin reads theme.typography to generate .prose, .prose-toc, + * and .prose-docs classes (and to make them @apply-able). See: + * https://github.com/tailwindlabs/tailwindcss-typography#customizing-the-css + */ + +module.exports = { + theme: { + extend: { + typography: { + DEFAULT: { + css: { + // Removal of backticks in code blocks. + // https://github.com/tailwindlabs/tailwindcss-typography/issues/135 + 'code::before': { content: '""' }, + 'code::after': { content: '""' }, + '--tw-prose-body': 'hsl(var(--foreground-light))', + '--tw-prose-headings': 'hsl(var(--foreground-default))', + '--tw-prose-lead': 'hsl(var(--foreground-light))', + '--tw-prose-links': 'hsl(var(--foreground-light))', + '--tw-prose-bold': 'hsl(var(--foreground-light))', + '--tw-prose-counters': 'hsl(var(--foreground-light))', + '--tw-prose-bullets': 'hsl(var(--foreground-muted))', + '--tw-prose-hr': 'hsl(var(--background-surface-300))', + '--tw-prose-quotes': 'hsl(var(--foreground-light))', + '--tw-prose-quote-borders': 'hsl(var(--background-surface-300))', + '--tw-prose-captions': 'hsl(var(--border-strong))', + '--tw-prose-code': 'hsl(var(--foreground-default))', + '--tw-prose-pre-code': 'hsl(var(--foreground-muted))', + '--tw-prose-pre-bg': 'hsl(var(--background-surface-200))', + '--tw-prose-th-borders': 'hsl(var(--background-surface-300))', + '--tw-prose-td-borders': 'hsl(var(--background-default))', + '--tw-prose-invert-body': 'hsl(var(--background-default))', + '--tw-prose-invert-headings': 'white', + '--tw-prose-invert-lead': 'hsl(var(--background-surface-300))', + '--tw-prose-invert-links': 'white', + '--tw-prose-invert-bold': 'white', + '--tw-prose-invert-counters': 'hsl(var(--background-surface-200))', + '--tw-prose-invert-bullets': 'hsl(var(--background-selection))', + '--tw-prose-invert-hr': 'hsl(var(--border-strong))', + '--tw-prose-invert-quotes': 'hsl(var(--background-alternative-default))', + '--tw-prose-invert-quote-borders': 'hsl(var(--border-strong))', + '--tw-prose-invert-captions': 'hsl(var(--background-surface-200))', + h4: { fontSize: '1.15em' }, + // h5 isn't included in --tw-prose-headings. + h5: { color: 'var(--color-scale-1200)' }, + 'h1, h2, h3, h4, h5, h6': { fontWeight: '400' }, + 'article h2, article h3, article h4, article h5, article h6': { + marginTop: '2em', + marginBottom: '1em', + }, + p: { fontWeight: '400' }, + strong: { fontWeight: '500' }, + pre: { + background: 'none', + padding: 0, + marginBottom: '32px', + }, + ul: { + listStyleType: 'none', + paddingLeft: '1rem', + }, + 'ul li': { position: 'relative' }, + 'ul li::before': { + position: 'absolute', + top: '0.75rem', + left: '-1rem', + height: '0.125rem', + width: '0.5rem', + borderRadius: '0.25rem', + backgroundColor: 'hsl(var(--border-strong))', + content: '""', + }, + ol: { + paddingLeft: '1rem', + counterReset: 'item', + listStyleType: 'none', + marginBottom: '3rem', + }, + 'ol>li': { + display: 'block', + position: 'relative', + paddingLeft: '1rem', + }, + 'ol>li::before': { + position: 'absolute', + top: '0.25rem', + left: '-1rem', + height: '1.2rem', + width: '1.2rem', + borderRadius: '0.25rem', + backgroundColor: 'hsl(var(--background-surface-100))', + border: '1px solid hsl(var(--border-default))', + content: 'counter(item) " "', + counterIncrement: 'item', + fontSize: '12px', + color: 'hsl(var(--foreground-muted))', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + 'p img': { + border: '1px solid hsl(var(--border-muted))', + borderRadius: '4px', + overflow: 'hidden', + }, + iframe: { + border: '1px solid hsl(var(--border-default))', + borderRadius: 'var(--radius-lg)', + }, + td: { + borderBottom: '1px solid hsl(var(--background-surface-200))', + }, + code: { + fontWeight: '400', + padding: '0.2rem 0.4rem', + backgroundColor: 'hsl(var(--background-surface-200))', + border: '1px solid hsl(var(--background-surface-300))', + borderRadius: 'var(--radius-lg)', + }, + a: { + position: 'relative', + transition: 'all 0.18s ease', + paddingBottom: '2px', + fontWeight: '400', + opacity: 1, + color: 'hsl(var(--foreground-default))', + textDecorationLine: 'underline', + textDecorationColor: 'hsl(var(--foreground-muted))', + textDecorationThickness: '1px', + textUnderlineOffset: '2px', + }, + 'a:hover': { + textDecorationColor: 'hsl(var(--foreground-default))', + }, + figcaption: { + color: 'hsl(var(--foreground-muted))', + fontFamily: 'Office Code Pro, monospace', + }, + 'figure.quote-figure p:first-child': { + marginTop: '0 !important', + }, + 'figure.quote-figure p:last-child': { + marginBottom: '0 !important', + }, + figure: { margin: '3rem 0' }, + 'figure img': { margin: '0 !important' }, + }, + }, + + toc: { + css: { + ul: { + 'list-style-type': 'none', + 'padding-left': 0, + margin: 0, + li: { 'padding-left': 0 }, + a: { + display: 'block', + marginBottom: '0.4rem', + 'text-decoration': 'none', + fontSize: '0.8rem', + fontWeight: '200', + color: 'hsl(var(--foreground-light))', + '&:hover': { + color: 'hsl(var(--foreground-default))', + }, + 'font-weight': '400', + }, + ul: { + 'list-style-type': 'none', + li: { + marginTop: '0.2rem', + marginBottom: '0.2rem', + 'padding-left': '0 !important', + 'margin-left': '0.5rem', + }, + a: { + fontWeight: '200', + color: 'hsl(var(--foreground-lighter))', + '&:hover': { + color: 'hsl(var(--foreground-default))', + }, + }, + }, + }, + }, + }, + + // Used in docs and changelog content. + docs: { + css: { + '--tw-prose-body': 'hsl(var(--foreground-light))', + '--tw-prose-headings': 'hsl(var(--foreground-default))', + '--tw-prose-lead': 'hsl(var(--foreground-light))', + '--tw-prose-links': 'hsl(var(--brand-500))', + '--tw-prose-bold': 'hsl(var(--foreground-light))', + '--tw-prose-counters': 'hsl(var(--foreground-light))', + '--tw-prose-bullets': 'hsl(var(--foreground-muted))', + '--tw-prose-hr': 'hsl(var(--background-surface-300))', + '--tw-prose-quotes': 'hsl(var(--foreground-light))', + '--tw-prose-quote-borders': 'hsl(var(--background-surface-300))', + '--tw-prose-captions': 'hsl(var(--border-strong))', + '--tw-prose-code': 'hsl(var(--foreground-default))', + '--tw-prose-pre-code': 'hsl(var(--foreground-muted))', + '--tw-prose-pre-bg': 'hsl(var(--background-surface-200))', + '--tw-prose-th-borders': 'hsl(var(--background-surface-300))', + '--tw-prose-td-borders': 'hsl(var(--background-default))', + '--tw-prose-invert-body': 'hsl(var(--background-default))', + '--tw-prose-invert-headings': 'white', + '--tw-prose-invert-lead': 'hsl(var(--background-surface-300))', + '--tw-prose-invert-links': 'white', + '--tw-prose-invert-bold': 'white', + '--tw-prose-invert-counters': 'hsl(var(--background-surface-200))', + '--tw-prose-invert-bullets': 'hsl(var(--background-selection))', + '--tw-prose-invert-hr': 'hsl(var(--border-strong))', + '--tw-prose-invert-quotes': 'hsl(var(--background-alternative-default))', + '--tw-prose-invert-quote-borders': 'hsl(var(--border-strong))', + '--tw-prose-invert-captions': 'hsl(var(--background-surface-200))', + 'h1, h2, h3, h4, h5': { fontWeight: '400' }, + }, + }, + }, + }, + }, +} diff --git a/packages/config/ui.config.js b/packages/config/ui.config.js deleted file mode 100644 index 2c84db05b6c..00000000000 --- a/packages/config/ui.config.js +++ /dev/null @@ -1,441 +0,0 @@ -const deepMerge = require('deepmerge') -const forms = require('@tailwindcss/forms') -const radixUiColors = require('@radix-ui/colors') -const brandColors = require('./default-colors') -const svgToDataUri = require('mini-svg-data-uri') - -function flattenColorPalette(colors) { - return Object.assign( - {}, - ...Object.entries(colors ?? {}).flatMap(([key, value]) => - typeof value === 'object' - ? Object.entries(flattenColorPalette(value)).map(([k, v]) => ({ - [key + (k === 'DEFAULT' ? '' : '-' + k)]: v, - })) - : [{ [key]: value }] - ) - ) -} - -// exclude these colors from the included set from Radix -const excludedRadixColors = [ - 'bronze', - 'brown', - 'cyan', - 'grass', - 'olive', - 'mauve', - 'mint', - 'lime', - 'plum', - 'sage', - 'sand', - 'sky', - 'teal', -] - -// generates fixed scales -// based on the root/light mode version -const fixedOptions = ['scale', 'scaleA'] - -function radixColorKeys() { - let keys = Object.keys(radixUiColors) - - /** - * Filter array items based on search criteria (query) - */ - function filterItems(arr, query) { - return arr.filter(function (el) { - return el.toLowerCase().indexOf(query.toLowerCase()) == -1 - }) - } - - keys = filterItems(keys, 'Dark') - - // remove excluded colors - keys = keys.filter( - (key) => !excludedRadixColors.some((excludeColor) => key.startsWith(excludeColor)) - ) - - return keys -} - -function generateColorClasses() { - const brandColors = ['scale', 'scaleA'] - const colors = [...radixColorKeys(), ...brandColors] - - let mappedColors = {} - - // generate the shape of the colors object - colors.map((x) => { - // create empty obj for each color - mappedColors[x] = {} - // create empty obj for each fixed color - if (fixedOptions.some((v) => x.indexOf(v) >= 0)) { - mappedColors[`${x}-fixed`] = {} - } - }) - - colors.map((x) => { - for (let index = 0; index < 12; index++) { - const step = index + 1 - mappedColors[x][step * 100] = `var(--colors-${x}${step})` - - if (fixedOptions.some((v) => x.indexOf(v) >= 0)) { - mappedColors[`${x}-fixed`][step * 100] = `var(--colors-fixed-${x}${step})` - } - } - }) - - return mappedColors -} - -const colorClasses = generateColorClasses() - -// generate the CSS variables for tailwind to use -function generateCssVariables() { - // potential options - // { fixedOptions, brandColors } - - let rootColors = {} - let darkColors = {} - - const radixArray = Object.entries(radixUiColors) - .filter( - ([key, value]) => !excludedRadixColors.some((excludeColor) => key.startsWith(excludeColor)) - ) - .map(([, value]) => value) - - const brandArray = Object.values(brandColors) - - function generateColors(colors, index, colorSet) { - const key = Object.keys(colorSet)[index] - - if (key.includes('Dark')) { - darkColors = { ...darkColors, ...colors } - } else { - rootColors = { ...rootColors, ...colors } - - // generate an optional 'fixed' scale of colors - if ( - fixedOptions.some(function (v) { - return key.indexOf(v) >= 0 - }) - ) { - rootColors.fixed = { ...rootColors?.fixed, ...colors } - } - } - } - - radixArray.map((x, i) => { - generateColors(x, i, radixUiColors) - }) - - brandArray.map((x, i) => { - generateColors(x, i, brandColors) - }) - - return { - root: { ...rootColors }, - dark: { ...darkColors }, - } -} - -const variables = generateCssVariables() - -const uiConfig = { - theme: { - variables: { - DEFAULT: { - width: { - listbox: '320px', - }, - colors: { ...variables.root }, - }, - "[data-theme*='dark']": { - colors: { ...variables.dark }, - }, - }, - extend: { - // dropdown extensions - transformOrigin: { - // tailwind class for this is `origin-dropdown` - dropdown: 'var(--radix-dropdown-menu-content-transform-origin)', - popover: 'var(--radix-popover-menu-content-transform-origin);', - }, - width: { - listbox: 'var(--width-listbox);', - }, - keyframes: { - fadeIn: { - '0%': { transform: 'scale(0.95)', opacity: 0 }, - '100%': { transform: 'scale(1)', opacity: 1 }, - }, - fadeOut: { - '0%': { transform: 'scale(1)', opacity: 1 }, - '100%': { transform: 'scale(0.95)', opacity: 0 }, - }, - overlayContentShow: { - '0%': { opacity: 0, transform: 'translate(0%, -2%) scale(1)' }, - '100%': { opacity: 1, transform: 'translate(0%, 0%) scale(1)' }, - }, - overlayContentHide: { - '0%': { opacity: 1, transform: 'translate(0%, 0%) scale(1)' }, - '100%': { opacity: 0, transform: 'translate(0%, -2%) scale(1)' }, - }, - dropdownFadeIn: { - '0%': { transform: 'scale(0.95)', opacity: 0 }, - '100%': { transform: 'scale(1)', opacity: 1 }, - }, - dropdownFadeOut: { - '0%': { transform: 'scale(1)', opacity: 1 }, - '100%': { transform: 'scale(0.95)', opacity: 0 }, - }, - fadeInOverlayBg: { - '0%': { opacity: 0 }, - '100%': { opacity: 0.75 }, - }, - fadeOutOverlayBg: { - '0%': { opacity: 0.75 }, - '100%': { opacity: 0 }, - }, - slideDown: { - '0%': { height: 0, opacity: 0 }, - '100%': { - height: 'var(--radix-accordion-content-height)', - opacity: 1, - }, - }, - slideUp: { - '0%': { height: 'var(--radix-accordion-content-height)', opacity: 1 }, - '100%': { height: 0, opacity: 0 }, - }, - - slideDownNormal: { - '0%': { height: 0, opacity: 0 }, - '100%': { - height: 'inherit', - opacity: 1, - }, - }, - slideUpNormal: { - '0%': { height: 'inherit', opacity: 1 }, - '100%': { height: 0, opacity: 0 }, - }, - - panelSlideLeftOut: { - '0%': { transform: 'translateX(-100%)', opacity: 0 }, - '100%': { - transform: 'translate-x-0', - opacity: 1, - }, - }, - panelSlideLeftIn: { - '0%': { transform: 'translate-x-0', opacity: 1 }, - '100%': { transform: 'translateX(-100%)', opacity: 0 }, - }, - panelSlideRightOut: { - '0%': { transform: 'translateX(100%)', opacity: 0 }, - '100%': { - transform: 'translate-x-0', - opacity: 1, - }, - }, - panelSlideRightIn: { - '0%': { transform: 'translate-x-0', opacity: 1 }, - '100%': { transform: 'translateX(100%)', opacity: 0 }, - }, - lineLoading: { - '0%': { - marginLeft: '-10%', - width: '80px', - }, - '25%': { - width: ' 240px', - }, - '50%': { - marginLeft: '100%', - width: '80px', - }, - '75%': { - width: '240px', - }, - '100%': { - marginLeft: '-10%', - width: '80px', - }, - }, - }, - animation: { - 'fade-in': 'fadeIn 300ms both', - 'fade-out': 'fadeOut 300ms both', - - 'dropdown-content-show': 'overlayContentShow 100ms cubic-bezier(0.16, 1, 0.3, 1)', - 'dropdown-content-hide': 'overlayContentHide 100ms cubic-bezier(0.16, 1, 0.3, 1)', - - 'overlay-show': 'overlayContentShow 300ms cubic-bezier(0.16, 1, 0.3, 1)', - 'overlay-hide': 'overlayContentHide 300ms cubic-bezier(0.16, 1, 0.3, 1)', - - 'fade-in-overlay-bg': 'fadeInOverlayBg 300ms', - 'fade-out-overlay-bg': 'fadeOutOverlayBg 300ms', - - 'slide-down': 'slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1)', - 'slide-up': 'slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1)', - - 'slide-down-normal': 'slideDownNormal 300ms cubic-bezier(0.87, 0, 0.13, 1)', - 'slide-up-normal': 'slideUpNormal 300ms cubic-bezier(0.87, 0, 0.13, 1)', - - 'panel-slide-left-out': 'panelSlideLeftOut 200ms cubic-bezier(0.87, 0, 0.13, 1)', - 'panel-slide-left-in': 'panelSlideLeftIn 250ms cubic-bezier(0.87, 0, 0.13, 1)', - 'panel-slide-right-out': 'panelSlideRightOut 200ms cubic-bezier(0.87, 0, 0.13, 1)', - 'panel-slide-right-in': 'panelSlideRightIn 250ms cubic-bezier(0.87, 0, 0.13, 1)', - - 'line-loading': 'lineLoading 1.8s infinite', - 'line-loading-slower': 'lineLoading 2.3s infinite', - - // tailwind class for this is `animate-dropdownFadeIn` - dropdownFadeIn: 'dropdownFadeIn 0.1s ease-out', - // tailwind class for this is `animate-dropdownFadeOut` - dropdownFadeOut: 'dropdownFadeOut 0.1s ease-out', - }, - colors: { - ...colorClasses, - 'hi-contrast': `hsl(var(--foreground-default))`, - 'lo-contrast': `hsl(var(--background-alternative-default))`, - warning: { - default: 'red', - 100: '#342355', - }, - }, - }, - }, - plugins: [ - require('@mertasan/tailwindcss-variables'), - function ({ addBase, addUtilities, addComponents, addVariant }) { - // hide the up/down arrows in number inputs for webkit browsers - addBase({ - 'input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button': - { - '-webkit-appearance': 'none', - margin: '0', - }, - }) - addBase({ - 'input[type=number]': { - '-moz-appearance': 'textfield', - }, - }) - // [Ivan] Not sure if it's used. - addComponents({ - ".dropdown-content[data-state='open']": { - animation: 'fadeIn 50ms ease-out', - }, - ".dropdown-content[data-state='closed']": { - animation: 'fadeOut 50ms ease-in', - }, - }) - addUtilities({ - '.line-loading-bg': { - background: 'rgb(0, 0, 0)', - background: - 'linear-gradient(90deg,rgba(0, 0, 0, 0) 0%,rgba(255, 255, 255, 0.65) 50%,rgba(0, 0, 0, 0) 100%)', - }, - '.line-loading-bg-light': { - background: 'rgb(0, 0, 0)', - background: - 'linear-gradient(90deg,rgba(0, 0, 0, 0) 0%,rgba(33, 33, 33, 0.65) 50%,rgba(0, 0, 0, 0) 100%)', - }, - '.no-scrollbar': { - /* Hide scrollbar for IE, Edge*/ - '-ms-overflow-style': 'none', - - /* Firefox */ - 'scrollbar-width': 'none' /* Firefox */, - - /* Hide scrollbar for Chrome, Safari and Opera */ - '&::-webkit-scrollbar': { - display: 'none', - }, - }, - /* Add fadeout effect */ - '.mask-fadeout-right': { - '-webkit-mask-image': 'linear-gradient(to right, white 98%, transparent 100%)', - 'mask-image': 'linear-gradient(to right, white 98%, transparent 100%)', - }, - '.mask-fadeout-left': { - '-webkit-mask-image': 'linear-gradient(to left, white 98%, transparent 100%)', - 'mask-image': 'linear-gradient(to left, white 98%, transparent 100%)', - }, - }) - addVariant('data-open-parent', '[data-state="open"] &') - addVariant('data-closed-parent', '[data-state="closed"] &') - addVariant('data-open', '&[data-state="open"]') - addVariant('data-closed', '&[data-state="closed"]') - addVariant('data-show', '&[data-state="show"]') - addVariant('data-hide', '&[data-state="hide"]') - addVariant('data-checked', '&[data-state="checked"]') - addVariant('data-unchecked', '&[data-state="unchecked"]') - addVariant('aria-expanded', '&[aria-expanded="true"]') - // addVariant('parent-data-open', '[data-state="open"]&') - }, - function ({ matchUtilities, theme }) { - matchUtilities( - { - highlight: (value) => ({ boxShadow: `inset 0 1px 0 0 ${value}` }), - }, - { values: flattenColorPalette(theme('backgroundColor')), type: 'color' } - ) - matchUtilities( - { - subhighlight: (value) => ({ - boxShadow: `inset 0 -1px 0 0 ${value}`, - }), - }, - { - values: flattenColorPalette(theme('backgroundColor')), - type: 'color', - } - ) - matchUtilities( - { - 'bg-grid': (value) => ({ - backgroundImage: `url("${svgToDataUri( - `` - )}")`, - }), - }, - { values: flattenColorPalette(theme('backgroundColor')), type: 'color' } - ) - }, - require('tailwindcss-radix')(), - forms, - ], -} - -function arrayMergeFn(destinationArray, sourceArray) { - return destinationArray.concat(sourceArray).reduce((acc, cur) => { - if (acc.includes(cur)) return acc - return [...acc, cur] - }, []) -} - -/** - * Merge Supabase UI and Tailwind CSS configurations - * @param {object} tailwindConfig - Tailwind config object - * @return {object} new config object - */ -function wrapper(tailwindConfig) { - let purge - if (Array.isArray(tailwindConfig.purge)) { - purge = { - content: tailwindConfig.purge, - } - } else { - purge = tailwindConfig.purge - } - return deepMerge({ ...tailwindConfig, purge }, uiConfig, { - arrayMerge: arrayMergeFn, - }) -} - -module.exports = wrapper diff --git a/packages/dev-tools/tailwind.config.css b/packages/dev-tools/tailwind.config.css new file mode 100644 index 00000000000..eeb08307b6f --- /dev/null +++ b/packages/dev-tools/tailwind.config.css @@ -0,0 +1,4 @@ +/* Placeholder so the VS Code Tailwind extension picks up IntelliSense in this + * package. Apps own their actual entry CSS file; this file just anchors the + * shared config for editor tooling. */ +@import 'config/tailwind.config.css'; diff --git a/packages/marketing/tailwind.config.css b/packages/marketing/tailwind.config.css new file mode 100644 index 00000000000..eeb08307b6f --- /dev/null +++ b/packages/marketing/tailwind.config.css @@ -0,0 +1,4 @@ +/* Placeholder so the VS Code Tailwind extension picks up IntelliSense in this + * package. Apps own their actual entry CSS file; this file just anchors the + * shared config for editor tooling. */ +@import 'config/tailwind.config.css'; diff --git a/packages/ui-patterns/src/multi-select/multi-select.tsx b/packages/ui-patterns/src/multi-select/multi-select.tsx index bc01b120aaf..fcdc6baff5a 100644 --- a/packages/ui-patterns/src/multi-select/multi-select.tsx +++ b/packages/ui-patterns/src/multi-select/multi-select.tsx @@ -46,7 +46,7 @@ const DROPDOWN_GAP = 8 const commandItemClass = cn( 'relative text-foreground-lighter text-left px-2 py-1.5 rounded-sm', 'hover:text-foreground hover:!bg-overlay-hover w-full flex items-center space-x-2', - 'peer-data-[value=true]:bg-overlay-hover peer-data-[value=true]:text-strong' + 'peer-data-[value=true]:bg-overlay-hover' ) function useMultiSelect() { diff --git a/packages/ui-patterns/tailwind.config.css b/packages/ui-patterns/tailwind.config.css new file mode 100644 index 00000000000..eeb08307b6f --- /dev/null +++ b/packages/ui-patterns/tailwind.config.css @@ -0,0 +1,4 @@ +/* Placeholder so the VS Code Tailwind extension picks up IntelliSense in this + * package. Apps own their actual entry CSS file; this file just anchors the + * shared config for editor tooling. */ +@import 'config/tailwind.config.css'; diff --git a/packages/ui-patterns/tailwind.config.js b/packages/ui-patterns/tailwind.config.js deleted file mode 100644 index fd65503e32a..00000000000 --- a/packages/ui-patterns/tailwind.config.js +++ /dev/null @@ -1 +0,0 @@ -// Placeholder so that VSCode tailwind extension enables intellisense in this project diff --git a/packages/ui/src/components/shadcn/ui/command.tsx b/packages/ui/src/components/shadcn/ui/command.tsx index 068bb834b7a..170c6b245ea 100644 --- a/packages/ui/src/components/shadcn/ui/command.tsx +++ b/packages/ui/src/components/shadcn/ui/command.tsx @@ -148,7 +148,7 @@ const CommandItem = React.forwardRef< =12.13.0'} - peerDependencies: - autoprefixer: ^10.0.2 - postcss: ^8.5.10 - '@mjackson/headers@0.11.1': resolution: {integrity: sha512-uXXhd4rtDdDwkqAuGef1nuafkCa1NlTmEc1Jzc0NL4YiA1yON1NFXuqJ3hOuKvNKQwkiDwdD+JJlKVyz4dunFA==} @@ -16462,9 +16443,6 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss-radix@2.8.0: - resolution: {integrity: sha512-1k1UfoIYgVyBl13FKwwoKavjnJ5VEaUClCTAsgz3VLquN4ay/lyaMPzkbqD71sACDs2fRGImytAUlMb4TzOt1A==} - tailwindcss@4.2.4: resolution: {integrity: sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==} @@ -20476,12 +20454,6 @@ snapshots: dependencies: langium: 3.3.1 - '@mertasan/tailwindcss-variables@2.7.0(autoprefixer@10.4.27(postcss@8.5.10))(postcss@8.5.10)': - dependencies: - autoprefixer: 10.4.27(postcss@8.5.10) - lodash: 4.18.1 - postcss: 8.5.10 - '@mjackson/headers@0.11.1': {} '@mjackson/multipart-parser@0.10.1': @@ -34811,8 +34783,6 @@ snapshots: dependencies: tailwindcss: 4.2.4 - tailwindcss-radix@2.8.0: {} - tailwindcss@4.2.4: {} tapable@2.3.0: {}