mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 19:35:06 +03:00
## What kind of change does this PR introduce? Changes the base font weight for strong tags within the tailwind/typography configuration. ## What is the current behavior? I noticed that texts within our strong tags had an irregular stroke, and problems on rendering. The main problem is we are using the default 600 weight value coming from the config of tailwind, while our Circular custom font supports 400 and 600. _See the first text in the list from the screen just, like "Multi Protocol"._ <img width="717" height="555" alt="Screenshot 2026-02-25 at 16 10 29" src="https://github.com/user-attachments/assets/1063f049-ba9d-45a4-9946-ec4a0e380c48" /> ## What is the new behavior? Strong tags now render with 500 font weight to prevent bad text rendering. <img width="674" height="505" alt="Screenshot 2026-02-25 at 16 12 56" src="https://github.com/user-attachments/assets/383e895d-e2b7-4d92-9357-403396ca2722" />
547 lines
19 KiB
JavaScript
547 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',
|
|
},
|
|
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'),
|
|
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
|