Files
supabase/packages/config/tailwind.config.js
Jonathan Summers-Muir 0f9a915092 Revert code style in tailwind (#34835)
Update tailwind.config.js
2025-04-08 16:19:31 +00:00

543 lines
19 KiB
JavaScript

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} / <alpha-value>)` // 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
* adds <alpha-value> as part of the hsl value
*/
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} / <alpha-value>)`,
}
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} / <alpha-value>)`,
}
}
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({
mode: 'JIT',
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)/ <alpha-value>)`,
}),
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',
},
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',
},
// 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'),
plugin(motionSafeTransition),
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: <div className="transition-safe duration-safe-100">
* - 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
return [...acc, cur]
}, [])
}
/**
* Merge Supabase UI and Tailwind CSS configurations
* @param {object} tailwindConfig - Tailwind config object
* @return {object} new config object
*/
function wrapper(tailwindConfig) {
return deepMerge({ ...tailwindConfig }, uiConfig, {
arrayMerge: arrayMergeFn,
})
}
module.exports = wrapper