diff --git a/apps/expo/app/_layout.tsx b/apps/expo/app/_layout.tsx
index 8beb12aa3..e4ba45445 100644
--- a/apps/expo/app/_layout.tsx
+++ b/apps/expo/app/_layout.tsx
@@ -13,8 +13,8 @@ SplashScreen.preventAutoHideAsync()
export default function RootLayout() {
const [loaded, error] = useFonts({
- Inter: require('@tamagui/font-inter/otf/Inter-Medium.otf'),
- InterBold: require('@tamagui/font-inter/otf/Inter-Bold.otf'),
+ // Inter: require('@tamagui/font-inter/otf/Inter-Medium.otf'),
+ // InterBold: require('@tamagui/font-inter/otf/Inter-Bold.otf'),
})
// Expo Router uses Error Boundaries to catch errors in the navigation tree.
diff --git a/apps/expo/package.json b/apps/expo/package.json
index b5102b254..083684a7e 100755
--- a/apps/expo/package.json
+++ b/apps/expo/package.json
@@ -49,7 +49,7 @@
"devDependencies": {
"@babel/core": "^7.23.2",
"@expo/metro-config": "^0.10.7",
- "@tamagui/babel-plugin": "1.75.9",
+ "@tamagui/babel-plugin": "1.88.18",
"metro-minify-terser": "^0.80.0",
"typescript": "^5.2.2"
}
diff --git a/apps/next/app/layout.tsx b/apps/next/app/layout.tsx
index 2ada56661..c37cc01df 100644
--- a/apps/next/app/layout.tsx
+++ b/apps/next/app/layout.tsx
@@ -10,8 +10,8 @@ if (typeof requestAnimationFrame === 'undefined') {
}
}
import '@tamagui/core/reset.css'
-import '@tamagui/font-inter/css/400.css'
-import '@tamagui/font-inter/css/700.css'
+// import '@tamagui/font-inter/css/400.css'
+// import '@tamagui/font-inter/css/700.css'
import { Provider } from 'app/provider'
import { StylesProvider } from './styles-provider'
diff --git a/apps/next/next-env.d.ts b/apps/next/next-env.d.ts
index fd36f9494..4f11a03dc 100644
--- a/apps/next/next-env.d.ts
+++ b/apps/next/next-env.d.ts
@@ -1,6 +1,5 @@
///
///
-///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/apps/next/next.config.js b/apps/next/next.config.js
index 711208efc..b40677d6d 100644
--- a/apps/next/next.config.js
+++ b/apps/next/next.config.js
@@ -35,12 +35,16 @@ const plugins = [
withPWA,
withTamagui({
appDir: true,
+ emitSingleCSSFile: false,
+ doesMutateThemes: false,
config: './tamagui.config.ts',
components: ['tamagui', '@t4/ui'],
importsWhitelist: ['constants.js', 'colors.js'],
outputCSS: process.env.NODE_ENV === 'production' ? './public/tamagui.css' : null,
logTimings: true,
disableExtraction,
+ enableCSSOptimizations: true,
+ excludeReactNativeWebExports: ['Switch', 'ProgressBar', 'Picker', 'CheckBox', 'Touchable'],
shouldExtract: (path) => {
if (path.includes(join('packages', 'app'))) {
return true
diff --git a/apps/next/package.json b/apps/next/package.json
index d7296dd03..1c035e788 100644
--- a/apps/next/package.json
+++ b/apps/next/package.json
@@ -14,10 +14,11 @@
"dependencies": {
"@cloudflare/next-on-pages": "1.8.3",
"@ducanh2912/next-pwa": "^9.7.2",
+ "@fullhuman/postcss-purgecss": "^5.0.0",
"@supabase/auth-helpers-nextjs": "^0.7.4",
"@supabase/auth-helpers-react": "^0.4.2",
"@t4/ui": "workspace:*",
- "@tamagui/next-theme": "1.75.9",
+ "@tamagui/next-theme": "1.88.18",
"@tsndr/cloudflare-worker-jwt": "2.2.7",
"app": "workspace:*",
"million": "2.6.4",
@@ -25,14 +26,17 @@
"next-seo": "^6.4.0",
"next-superjson-plugin": "^0.5.10",
"pattycake": "^0.0.2",
+ "postcss": "^8.4.33",
+ "postcss-flexbugs-fixes": "^5.0.2",
+ "postcss-preset-env": "^9.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-native": "^0.72.6",
- "react-native-web-lite": "1.75.9",
+ "react-native-web": "^0.19.10",
"webpack": "^5.88.2"
},
"devDependencies": {
- "@tamagui/next-plugin": "1.75.9",
+ "@tamagui/next-plugin": "1.88.18",
"@types/react": "^18.2.37",
"vercel": "33.0.2",
"wrangler": "3.22.3"
diff --git a/apps/next/pages/ssr/index.tsx b/apps/next/pages/ssr/index.tsx
deleted file mode 100644
index 815f2dafb..000000000
--- a/apps/next/pages/ssr/index.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Paragraph, YStack } from '@t4/ui'
-import { GetServerSideProps } from 'next'
-
-export const runtime = 'experimental-edge'
-
-export const getServerSideProps = (async () => {
- return { props: { content: 'This content is sent from the server' } }
-}) satisfies GetServerSideProps<{ content: string }>
-
-export default function Page(props: { content: string }) {
- return (
-
- Server-side rendering
- {props.content}
-
- )
-}
diff --git a/apps/next/postcss.config.js b/apps/next/postcss.config.js
new file mode 100644
index 000000000..acd8a906d
--- /dev/null
+++ b/apps/next/postcss.config.js
@@ -0,0 +1,29 @@
+module.exports = {
+ plugins: [
+ 'postcss-flexbugs-fixes',
+ [
+ 'postcss-preset-env',
+ {
+ autoprefixer: {
+ flexbox: 'no-2009',
+ },
+ stage: 3,
+ features: {
+ 'custom-properties': false,
+ },
+ },
+ ],
+ // [
+ // '@fullhuman/postcss-purgecss',
+ // {
+ // content: [
+ // './pages/**/*.{js,jsx,ts,tsx}',
+ // './../../packages/app/**/*.{js,jsx,ts,tsx}',
+ // './../../packages/ui/**/*.{js,jsx,ts,tsx}',
+ // ],
+ // defaultExtractor: (content) => content.match(/[\w-/:]+(? Math.round(size * 1.1),
+// sizeLineHeight: (size) => Math.round(size * 1.1 + (size > 20 ? 10 : 10)),
+// }
+// )
+
+const webFontFamily =
+ 'system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'
+
+const headingFont = createFont({
+ family: isWeb ? webFontFamily : 'System',
size: {
- 6: 15,
+ 2: 12,
+ 3: 14,
+ 4: 16,
+ 5: 18,
+ 6: 20,
+ 7: 24,
+ 8: 28,
+ 9: 32,
+ 10: 48,
+ },
+ lineHeight: {
+ 2: 14,
+ 3: 16,
+ 4: 18,
+ 5: 20,
+ 6: 24,
+ 7: 28,
+ 8: 32,
+ 9: 40,
+ 10: 48,
},
transform: {
6: 'uppercase',
@@ -23,32 +92,59 @@ const headingFont = createInterFont({
7: '$color',
},
letterSpacing: {
- 5: 2,
- 6: 1,
- 7: 0,
- 8: -1,
- 9: -2,
- 10: -3,
+ 5: 3,
+ 6: 2,
+ 7: 1,
+ 8: 0,
+ 9: -1,
+ 10: -2,
12: -4,
- 14: -5,
- 15: -6,
- },
- face: {
- 700: { normal: 'InterBold' },
+ 14: -6,
+ 15: -7,
},
+ // face: {
+ // 700: { normal: 'InterBold' },
+ // },
})
-const bodyFont = createInterFont(
- {
- face: {
- 700: { normal: 'InterBold' },
- },
+const bodyFont = createFont({
+ family: isWeb ? webFontFamily : 'System',
+ size: {
+ // 2: 12,
+ // 3: 14,
+ // 4: 16,
+ // 5: 18,
+ // 7: 22,
+ // 8: 26,
+ // 9: 32,
+ // 10: 38,
+ 1: 12,
+ 2: 14,
+ 3: 12,
+ 4: 15,
+ 5: 24,
+ 7: 30,
+ 8: 36,
+ 9: 40,
+ 10: 52,
+ },
+ letterSpacing: {},
+ weight: {
+ 1: '300',
+ 4: '400',
},
- {
- sizeSize: (size) => Math.round(size * 1.1),
- sizeLineHeight: (size) => Math.round(size * 1.1 + (size > 20 ? 10 : 10)),
- }
-)
+ lineHeight: {
+ 1: 14,
+ 2: 21,
+ 3: 24,
+ 4: 27,
+ 5: 36,
+ 7: 45,
+ 8: 54,
+ 9: 60,
+ 10: 78,
+ },
+})
export const config = createTamagui({
defaultFont: 'body',
diff --git a/packages/ui/src/themes.ts b/packages/ui/src/themes.ts
new file mode 100644
index 000000000..d9eee681e
--- /dev/null
+++ b/packages/ui/src/themes.ts
@@ -0,0 +1,663 @@
+import { createThemeBuilder, MaskOptions } from '@tamagui/theme-builder'
+import { masks } from '@tamagui/themes/v2-themes'
+import {
+ blue,
+ blueDark,
+ gray,
+ grayDark,
+ green,
+ greenDark,
+ orange,
+ orangeDark,
+ pink,
+ pinkDark,
+ purple,
+ purpleDark,
+ red,
+ redDark,
+ yellow,
+ yellowDark,
+} from '@tamagui/colors'
+import { Variable, createTokens } from '@tamagui/web'
+
+const enabledThemes = {
+ blue: false,
+ gray: false,
+ green: false,
+ orange: false,
+ pink: false,
+ purple: false,
+ red: false,
+ yellow: false,
+}
+
+const colorTokens = {
+ light: {
+ ...(enabledThemes.blue ? { blue } : {}),
+ ...(enabledThemes.gray ? { gray } : {}),
+ ...(enabledThemes.green ? { green } : {}),
+ ...(enabledThemes.orange ? { orange } : {}),
+ ...(enabledThemes.pink ? { pink } : {}),
+ ...(enabledThemes.purple ? { purple } : {}),
+ ...(enabledThemes.red ? { red } : {}),
+ ...(enabledThemes.yellow ? { yellow } : {}),
+ },
+ dark: {
+ ...(enabledThemes.blue ? { blueDark } : {}),
+ ...(enabledThemes.gray ? { grayDark } : {}),
+ ...(enabledThemes.green ? { greenDark } : {}),
+ ...(enabledThemes.orange ? { orangeDark } : {}),
+ ...(enabledThemes.pink ? { pinkDark } : {}),
+ ...(enabledThemes.purple ? { purpleDark } : {}),
+ ...(enabledThemes.red ? { redDark } : {}),
+ ...(enabledThemes.yellow ? { yellowDark } : {}),
+ },
+}
+
+export const palettes = (() => {
+ const lightTransparent = 'rgba(255,255,255,0)'
+ const darkTransparent = 'rgba(10,10,10,0)'
+
+ const transparent = (hsl: string, opacity = 0) =>
+ hsl.replace(`%)`, `%, ${opacity})`).replace(`hsl(`, `hsla(`)
+
+ const getColorPalette = (colors: Object, color = colors[0]): string[] => {
+ const colorPalette = Object.values(colors)
+
+ // were re-ordering these
+ const [head, tail] = [colorPalette.slice(0, 6), colorPalette.slice(colorPalette.length - 5)]
+
+ // add our transparent colors first/last
+ // and make sure the last (foreground) color is white/black rather than colorful
+ // this is mostly for consistency with the older theme-base
+ return [
+ transparent(colorPalette[0]),
+ ...head,
+ ...tail,
+ color,
+ transparent(colorPalette[colorPalette.length - 1]),
+ ]
+ }
+
+ const lightColor = 'hsl(0, 0%, 9.0%)'
+ const lightPalette = [
+ lightTransparent,
+ '#fff',
+ '#f8f8f8',
+ 'hsl(0, 0%, 96.3%)',
+ 'hsl(0, 0%, 94.1%)',
+ 'hsl(0, 0%, 92.0%)',
+ 'hsl(0, 0%, 90.0%)',
+ 'hsl(0, 0%, 88.5%)',
+ 'hsl(0, 0%, 81.0%)',
+ 'hsl(0, 0%, 56.1%)',
+ 'hsl(0, 0%, 50.3%)',
+ 'hsl(0, 0%, 42.5%)',
+ lightColor,
+ darkTransparent,
+ ]
+
+ const darkColor = '#fff'
+ const darkPalette = [
+ darkTransparent,
+ '#050505',
+ '#151515',
+ '#191919',
+ '#232323',
+ '#282828',
+ '#323232',
+ '#424242',
+ '#494949',
+ '#545454',
+ '#626262',
+ '#a5a5a5',
+ darkColor,
+ lightTransparent,
+ ]
+
+ const lightPalettes = objectFromEntries(
+ objectKeys(colorTokens.light).map(
+ (key) => [`light_${key}`, getColorPalette(colorTokens.light[key], lightColor)] as const
+ )
+ )
+
+ const darkPalettes = objectFromEntries(
+ objectKeys(colorTokens.dark).map(
+ (key) => [`dark_${key}`, getColorPalette(colorTokens.dark[key], darkColor)] as const
+ )
+ )
+
+ const colorPalettes = {
+ ...lightPalettes,
+ ...darkPalettes,
+ }
+
+ return {
+ light: lightPalette,
+ dark: darkPalette,
+ ...colorPalettes,
+ }
+})()
+
+const templateColorsSpecific = {
+ color1: 1,
+ color2: 2,
+ color3: 3,
+ color4: 4,
+ color5: 5,
+ color6: 6,
+ color7: 7,
+ color8: 8,
+ color9: 9,
+ color10: 10,
+ color11: 11,
+ color12: 12,
+}
+
+export const templates = (() => {
+ // templates use the palette and specify index
+ // negative goes backwards from end so -1 is the last item
+ const template = {
+ ...templateColorsSpecific,
+ // the background, color, etc keys here work like generics - they make it so you
+ // can publish components for others to use without mandating a specific color scale
+ // the @tamagui/button Button component looks for `$background`, so you set the
+ // dark_red_Button theme to have a stronger background than the dark_red theme.
+ background: 2,
+ backgroundHover: 3,
+ backgroundPress: 4,
+ backgroundFocus: 5,
+ backgroundStrong: 1,
+ backgroundTransparent: 0,
+ color: -1,
+ colorHover: -2,
+ colorPress: -1,
+ colorFocus: -2,
+ colorTransparent: -0,
+ borderColor: 5,
+ borderColorHover: 6,
+ borderColorFocus: 4,
+ borderColorPress: 5,
+ placeholderColor: -4,
+ }
+
+ return {
+ base: template,
+ colorLight: {
+ ...template,
+ // light color themes are a bit less sensitive
+ borderColor: 4,
+ borderColorHover: 5,
+ borderColorFocus: 4,
+ borderColorPress: 4,
+ },
+ }
+})()
+
+export const maskOptions = (() => {
+ const shadows = {
+ shadowColor: 0,
+ shadowColorHover: 0,
+ shadowColorPress: 0,
+ shadowColorFocus: 0,
+ }
+
+ const colors = {
+ ...shadows,
+ color: 0,
+ colorHover: 0,
+ colorFocus: 0,
+ colorPress: 0,
+ }
+
+ const baseMaskOptions: MaskOptions = {
+ override: shadows,
+ skip: shadows,
+ // avoids the transparent ends
+ max: palettes.light.length - 2,
+ min: 1,
+ }
+
+ const skipShadowsAndSpecificColors = {
+ ...shadows,
+ ...templateColorsSpecific,
+ }
+
+ return {
+ component: {
+ ...baseMaskOptions,
+ override: colors,
+ skip: skipShadowsAndSpecificColors,
+ },
+ alt: {
+ ...baseMaskOptions,
+ },
+ button: {
+ ...baseMaskOptions,
+ override: {
+ ...colors,
+ borderColor: 'transparent',
+ borderColorHover: 'transparent',
+ },
+ skip: skipShadowsAndSpecificColors,
+ },
+ } satisfies Record
+})()
+
+const lightShadowColor = 'rgba(0,0,0,0.04)'
+const lightShadowColorStrong = 'rgba(0,0,0,0.085)'
+const darkShadowColor = 'rgba(0,0,0,0.2)'
+const darkShadowColorStrong = 'rgba(0,0,0,0.3)'
+
+// should roughly map to button/input etc height at each level
+// fonts should match that height/lineHeight at each stop
+// so these are really non-linear on purpose
+// why?
+// - at sizes <1, used for fine grained things (borders, smallest paddingY)
+// - so smallest padY should be roughly 1-4px so it can join with lineHeight
+// - at sizes >=1, have to consider "pressability" (jumps up)
+// - after that it should go upwards somewhat naturally
+// - H1 / headings top out at 10 naturally, so after 10 we can go upwards faster
+// but also one more wrinkle...
+// space is used in conjunction with size
+// i'm setting space to generally just a fixed fraction of size (~1/3-2/3 still fine tuning)
+const size = {
+ $0: 0,
+ '$0.25': 2,
+ '$0.5': 4,
+ '$0.75': 8,
+ $1: 20,
+ '$1.5': 24,
+ $2: 28,
+ '$2.5': 32,
+ $3: 36,
+ '$3.5': 40,
+ $4: 44,
+ $true: 44,
+ '$4.5': 48,
+ $5: 52,
+ $6: 64,
+ $7: 74,
+ $8: 84,
+ $9: 94,
+ $10: 104,
+ $11: 124,
+ $12: 144,
+ $13: 164,
+ $14: 184,
+ $15: 204,
+ $16: 224,
+ $17: 224,
+ $18: 244,
+ $19: 264,
+ $20: 284,
+}
+
+type SizeKeysIn = keyof typeof size
+type Sizes = {
+ [Key in SizeKeysIn extends `$${infer Key}` ? Key : SizeKeysIn]: number
+}
+type SizeKeys = `${keyof Sizes extends `${infer K}` ? K : never}`
+
+const spaces = Object.entries(size).map(([k, v]) => {
+ return [k, sizeToSpace(v)] as const
+})
+
+const spacesNegative = spaces.slice(1).map(([k, v]) => [`-${k.slice(1)}`, -v])
+
+type SizeKeysWithNegatives =
+ | Exclude<`-${SizeKeys extends `$${infer Key}` ? Key : SizeKeys}`, '-0'>
+ | SizeKeys
+
+const space: {
+ [Key in SizeKeysWithNegatives]: Key extends keyof Sizes ? Sizes[Key] : number
+} = {
+ ...Object.fromEntries(spaces),
+ ...Object.fromEntries(spacesNegative),
+} as any
+
+const zIndex = {
+ 0: 0,
+ 1: 100,
+ 2: 200,
+ 3: 300,
+ 4: 400,
+ 5: 500,
+}
+
+const darkColors = {
+ ...(enabledThemes.blue ? colorTokens.dark.blue : {}),
+ ...(enabledThemes.gray ? colorTokens.dark.gray : {}),
+ ...(enabledThemes.green ? colorTokens.dark.green : {}),
+ ...(enabledThemes.orange ? colorTokens.dark.orange : {}),
+ ...(enabledThemes.pink ? colorTokens.dark.pink : {}),
+ ...(enabledThemes.purple ? colorTokens.dark.purple : {}),
+ ...(enabledThemes.red ? colorTokens.dark.red : {}),
+ ...(enabledThemes.yellow ? colorTokens.dark.yellow : {}),
+}
+
+const lightColors = {
+ ...(enabledThemes.blue ? colorTokens.light.blue : {}),
+ ...(enabledThemes.gray ? colorTokens.light.gray : {}),
+ ...(enabledThemes.green ? colorTokens.light.green : {}),
+ ...(enabledThemes.orange ? colorTokens.light.orange : {}),
+ ...(enabledThemes.pink ? colorTokens.light.pink : {}),
+ ...(enabledThemes.purple ? colorTokens.light.purple : {}),
+ ...(enabledThemes.red ? colorTokens.light.red : {}),
+ ...(enabledThemes.yellow ? colorTokens.light.yellow : {}),
+}
+
+const color = {
+ ...postfixObjKeys(lightColors, 'Light'),
+ ...postfixObjKeys(darkColors, 'Dark'),
+}
+
+const radius = {
+ 0: 0,
+ 1: 3,
+ 2: 5,
+ 3: 7,
+ 4: 9,
+ true: 9,
+ 5: 10,
+ 6: 16,
+ 7: 19,
+ 8: 22,
+ 9: 26,
+ 10: 34,
+ 11: 42,
+ 12: 50,
+}
+
+export const tokens = createTokens({
+ color,
+ radius,
+ zIndex,
+ space,
+ size,
+})
+
+const shadows = {
+ light: {
+ shadowColor: lightShadowColorStrong,
+ shadowColorHover: lightShadowColorStrong,
+ shadowColorPress: lightShadowColor,
+ shadowColorFocus: lightShadowColor,
+ },
+ dark: {
+ shadowColor: darkShadowColorStrong,
+ shadowColorHover: darkShadowColorStrong,
+ shadowColorPress: darkShadowColor,
+ shadowColorFocus: darkShadowColor,
+ },
+}
+
+const colorThemeDefinition = (colorName: string) => [
+ {
+ parent: 'light',
+ palette: colorName,
+ template: 'colorLight',
+ },
+ {
+ parent: 'dark',
+ palette: colorName,
+ template: 'base',
+ },
+]
+
+const nonInherited = {
+ light: {
+ ...lightColors,
+ ...shadows.light,
+ },
+ dark: {
+ ...darkColors,
+ ...shadows.dark,
+ },
+}
+
+const overlayThemeDefinitions = [
+ {
+ parent: 'light',
+ theme: {
+ background: 'rgba(0,0,0,0.5)',
+ },
+ },
+ {
+ parent: 'dark',
+ theme: {
+ background: 'rgba(0,0,0,0.9)',
+ },
+ },
+]
+
+// --- themeBuilder ---
+
+const themeBuilder = createThemeBuilder()
+ .addPalettes(palettes)
+ .addTemplates(templates)
+ .addMasks(masks)
+ .addThemes({
+ light: {
+ template: 'base',
+ palette: 'light',
+ nonInheritedValues: nonInherited.light,
+ },
+ dark: {
+ template: 'base',
+ palette: 'dark',
+ nonInheritedValues: nonInherited.dark,
+ },
+ })
+ .addChildThemes({
+ ...(enabledThemes.blue ? { blue: colorThemeDefinition('blue') } : {}),
+ ...(enabledThemes.gray ? { gray: colorThemeDefinition('gray') } : {}),
+ ...(enabledThemes.green ? { green: colorThemeDefinition('green') } : {}),
+ ...(enabledThemes.orange ? { orange: colorThemeDefinition('orange') } : {}),
+ ...(enabledThemes.pink ? { pink: colorThemeDefinition('pink') } : {}),
+ ...(enabledThemes.purple ? { purple: colorThemeDefinition('purple') } : {}),
+ ...(enabledThemes.red ? { red: colorThemeDefinition('red') } : {}),
+ ...(enabledThemes.yellow ? { yellow: colorThemeDefinition('yellow') } : {}),
+ })
+ .addChildThemes({
+ alt1: {
+ mask: 'soften',
+ ...maskOptions.alt,
+ },
+ alt2: {
+ mask: 'soften2Border1',
+ ...maskOptions.alt,
+ },
+ active: {
+ mask: 'soften3FlatBorder',
+ skip: {
+ color: 1,
+ },
+ },
+ })
+ .addChildThemes(
+ {
+ ListItem: [
+ {
+ parent: 'light',
+ avoidNestingWithin: ['active'],
+ mask: 'identity',
+ ...maskOptions.component,
+ },
+ {
+ parent: 'dark',
+ avoidNestingWithin: ['active'],
+ mask: 'identity',
+ ...maskOptions.component,
+ },
+ ],
+
+ Card: {
+ mask: 'soften',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ Button: {
+ mask: 'soften2Border1',
+ ...maskOptions.component,
+ },
+
+ Checkbox: {
+ mask: 'softenBorder2',
+ ...maskOptions.component,
+ },
+
+ Switch: {
+ mask: 'soften2Border1',
+ ...maskOptions.component,
+ },
+
+ SwitchThumb: {
+ mask: 'inverseStrengthen2',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ TooltipContent: {
+ mask: 'soften2Border1',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ DrawerFrame: {
+ mask: 'soften',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ Progress: {
+ mask: 'soften',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ RadioGroupItem: {
+ mask: 'softenBorder2',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ TooltipArrow: {
+ mask: 'soften',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ SliderTrackActive: {
+ mask: 'inverseSoften',
+ ...maskOptions.component,
+ },
+
+ SliderTrack: {
+ mask: 'soften2Border1',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ SliderThumb: {
+ mask: 'inverse',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ Tooltip: {
+ mask: 'inverse',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ ProgressIndicator: {
+ mask: 'inverse',
+ avoidNestingWithin: ['active'],
+ ...maskOptions.component,
+ },
+
+ SheetOverlay: overlayThemeDefinitions,
+ DialogOverlay: overlayThemeDefinitions,
+ ModalOverlay: overlayThemeDefinitions,
+
+ Input: {
+ mask: 'softenBorder2',
+ ...maskOptions.component,
+ },
+
+ TextArea: {
+ mask: 'softenBorder2',
+ ...maskOptions.component,
+ },
+ },
+ {
+ // to save bundle size but make alt themes not work on components
+ // avoidNestingWithin: ['alt1', 'alt2'],
+ }
+ )
+
+// --- main export ---
+
+export const themes = themeBuilder.build()
+
+/**
+ * if typescript too deep types :/
+ *
+
+const themesIn = themeBuilder.build()
+type ThemesIn = typeof themesIn
+type ThemesOut = Omit & {
+ light: ThemesIn['light'] & typeof nonInherited.light
+ dark: ThemesIn['dark'] & typeof nonInherited.dark
+}
+export const themes = themesIn as ThemesOut
+
+ **/
+
+// --- utils ---
+
+function postfixObjKeys | string }, B extends string>(
+ obj: A,
+ postfix: B
+): {
+ [Key in `${keyof A extends string ? keyof A : never}${B}`]: Variable | string
+} {
+ return Object.fromEntries(Object.entries(obj).map(([k, v]) => [`${k}${postfix}`, v])) as any
+}
+
+// a bit odd but keeping backward compat for values >8 while fixing below
+function sizeToSpace(v: number) {
+ if (v === 0) return 0
+ if (v === 2) return 0.5
+ if (v === 4) return 1
+ if (v === 8) return 1.5
+ if (v <= 16) return Math.round(v * 0.333)
+ return Math.floor(v * 0.7 - 12)
+}
+
+function objectFromEntries(arr: ARR_T): EntriesToObject {
+ return Object.fromEntries(arr) as EntriesToObject
+}
+
+type EntriesType = [PropertyKey, unknown][] | ReadonlyArray
+
+type DeepWritable = { -readonly [P in keyof OBJ_T]: DeepWritable }
+type UnionToIntersection = // From https://stackoverflow.com/a/50375286
+ (UNION_T extends any ? (k: UNION_T) => void : never) extends (k: infer I) => void ? I : never
+
+type UnionObjectFromArrayOfPairs =
+ DeepWritable extends (infer R)[]
+ ? R extends [infer key, infer val]
+ ? { [prop in key & PropertyKey]: val }
+ : never
+ : never
+type MergeIntersectingObjects = { [key in keyof ObjT]: ObjT[key] }
+type EntriesToObject = MergeIntersectingObjects<
+ UnionToIntersection>
+>
+
+function objectKeys(obj: O) {
+ return Object.keys(obj) as Array
+}