import { createVar, fallbackVar, style, styleVariants } from '@vanilla-extract/css';
import { typedKeys } from '#/lib/utils/objects';
import { colors } from '#/styles/colors';
import { components } from '#/styles/layers.css';
import { recipe } from '#/styles/recipe';
import { roundToPx } from '#/styles/round';
import { fontLeading, fontSize, fontWeight, lineHeight } from '#/styles/tokens.css';
const variantsFor = <Scale extends Record<string, number | string>, Property extends string>(
scale: Scale,
property: Property,
): { [Key in keyof Scale]: Record<Property, Scale[Key]> } => {
const out: Record<string, Record<string, number | string>> = {};
for (const [key, value] of Object.entries(scale)) {
out[key] = { [property]: value };
}
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- one entry per `scale` key; computed keys widen to an index signature
return out as { [Key in keyof Scale]: Record<Property, Scale[Key]> };
};
const textColors = {
text: colors.text,
textContrastHigh: colors.textContrastHigh,
textContrastLow: colors.textContrastLow,
textContrastMedium: colors.textContrastMedium,
textLink: colors.textLink,
white: colors.white,
contrast_400: colors.contrast_400,
primary_500: colors.primary_500,
primary_600: colors.primary_600,
primary_900: colors.primary_900,
negative_400: colors.negative_400,
negative_500: colors.negative_500,
negative_600: colors.negative_600,
};
export const fontSizeVar = createVar();
export const sizeLeadingVar = createVar();
export const leadingOverrideVar = createVar();
const fontSizeScale = fallbackVar(fontSizeVar, fontSize.md);
const pairedLeading = fallbackVar(sizeLeadingVar, String(fontLeading.md));
const leading = fallbackVar(leadingOverrideVar, pairedLeading);
const sizeVariants = (): { [K in keyof typeof fontLeading]: { vars: Record<string, string> } } => {
const out: Record<string, { vars: Record<string, string> }> = {};
for (const key of typedKeys(fontLeading)) {
out[key] = { vars: { [fontSizeVar]: fontSize[key], [sizeLeadingVar]: String(fontLeading[key]) } };
}
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the loop writes one entry per `fontLeading` key
return out as { [K in keyof typeof fontLeading]: { vars: Record<string, string> } };
};
export const text = recipe(
{
base: {
margin: 0,
padding: 0,
lineHeight: roundToPx(`calc(${fontSizeScale} * ${leading})`),
whiteSpace: 'pre-wrap',
overflowWrap: 'break-word',
fontFamily: 'inherit',
fontSize: fontSizeScale,
},
defaultVariants: { color: 'text', leading: 'snug', size: 'md' },
variants: {
align: { center: { textAlign: 'center' }, left: { textAlign: 'left' }, right: { textAlign: 'right' } },
color: variantsFor(textColors, 'color'),
leading: {
none: { vars: { [leadingOverrideVar]: String(lineHeight.none) } },
snug: { vars: { [leadingOverrideVar]: pairedLeading } },
},
size: sizeVariants(),
weight: variantsFor(fontWeight, 'fontWeight'),
},
},
{ debugId: 'text', layer: components },
);
export const lineClampVar = createVar();
export const clampSingleLine = style({
'@layer': {
[components]: {
minWidth: 0,
maxWidth: '100%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
},
},
});
export const clampMultiLine = style({
'@layer': {
[components]: {
display: '-webkit-box',
overflow: 'hidden',
textOverflow: 'ellipsis',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: lineClampVar,
},
},
});
export const userSelect = styleVariants({
none: { '@layer': { [components]: { userSelect: 'none' } } },
text: { '@layer': { [components]: { userSelect: 'text' } } },
});