Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/lukebennett88/luke-ui. ui.lukebennett.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394import { breakpoints, vars } from '@luke-ui/react/theme';import { visuallyHiddenStyle } from '@luke-ui/react/visually-hidden';import { globalStyle, keyframes, style } from '@vanilla-extract/css';import { SITE_HEADER_BLOCK_SIZE } from './site-header-size.js';
/** Sticky token panel headers sit below the site header. */const TOKEN_REFERENCE_PANEL_HEADER_INSET = SITE_HEADER_BLOCK_SIZE;
/** Viewport width below the token row's `bp768` grid. */const BELOW_TABLET = `(max-width: ${breakpoints.bp768 - 1}px)`;
const PANEL_BORDER = `1px solid ${vars.color.border.decorative}`;
// DS gap: Box has no overflowWrap prop. A long token path may break anywhere rather// than push the column wider than the page.export const tokenIdentification = style({ '@layer': { components: { overflowWrap: 'anywhere', }, },});
// DS gap: Box has no sticky positioning, z-index, or per-side border props. The header owns the// panel's top chrome (borders + outer top radius) so its fill is not clipped inside a parent// radius. Top radius stays constant while sticky.export const columnHeaderShell = style({ '@layer': { components: { backgroundColor: vars.color.surface.subdued, borderBlockStart: PANEL_BORDER, borderInlineEnd: PANEL_BORDER, borderInlineStart: PANEL_BORDER, borderStartEndRadius: vars.radius.surface, borderStartStartRadius: vars.radius.surface, insetBlockStart: TOKEN_REFERENCE_PANEL_HEADER_INSET, position: 'sticky', zIndex: 1, '@media': { [BELOW_TABLET]: { ...visuallyHiddenStyle, }, }, }, },});
// DS gap: Box has no per-side border props. A rule separates the header from the first row.export const columnHeader = style({ '@layer': { components: { borderBlockEnd: PANEL_BORDER, }, },});
// DS gap: Box has no per-side border or corner props. Body chrome sits under the sticky header:// side and bottom borders, bottom radii. Below tablet the sticky header is visually hidden, so the// body also draws the top border and top radii.export const panelBody = style({ '@layer': { components: { borderBlockEnd: PANEL_BORDER, borderEndEndRadius: vars.radius.surface, borderEndStartRadius: vars.radius.surface, borderInlineEnd: PANEL_BORDER, borderInlineStart: PANEL_BORDER, '@media': { [BELOW_TABLET]: { borderBlockStart: PANEL_BORDER, borderStartEndRadius: vars.radius.surface, borderStartStartRadius: vars.radius.surface, }, }, }, },});
// DS gap: Box has no listStyle prop. Token lists are lists for assistive technology, without// markers.export const list = style({ '@layer': { components: { listStyle: 'none', }, },});
// DS gap: Box has no per-side border props or sibling selectors. A rule separates each group of a// panel from the one before it.export const group = style({ '@layer': { components: { selectors: { '& + &': { borderBlockStart: `1px solid ${vars.color.border.decorative}`, }, }, }, },});
// DS gap: Box has no per-side border, sibling selector, or scroll margin props. A rule separates// rows, and hash targets clear the site header.export const row = style({ '@layer': { components: { scrollMarginBlockStart: `calc(${SITE_HEADER_BLOCK_SIZE} + ${vars.space['24']})`, selectors: { '& + &': { borderBlockStart: `1px solid ${vars.color.border.decorative}`, }, }, }, },});
const TYPE_CONTAINER = 'token-type';/** Below this inline size each token name sits above its value and copy control. */const TYPE_NARROW = `${TYPE_CONTAINER} (inline-size < 40rem)`;
// DS gap: Box has no containerType or containerName prop, and its responsive props resolve against// the root container, not the table's own width.export const typeFrame = style({ '@layer': { components: { containerName: TYPE_CONTAINER, containerType: 'inline-size', }, },});
// DS gap: Box has no tableLayout, borderCollapse, or borderSpacing prop, and no container-query// display. Separate borders keep the frame's radius. The narrow layout stacks each row's token// identification above its value, while the table headers remain in the accessibility tree.export const typeTable = style({ '@layer': { components: { borderCollapse: 'separate', borderSpacing: 0, tableLayout: 'fixed', '@container': { [TYPE_NARROW]: { display: 'block', tableLayout: 'auto', }, }, }, },});
// DS gap: Box has no container-query display or viewport media queries. The visible sticky header// is `TokenColumnHeader`; this thead stays in the accessibility tree and shows below tablet when// the panel is wide enough for columns.export const typeHead = style({ '@layer': { components: { '@media': { [`(min-width: ${breakpoints.bp768}px)`]: { ...visuallyHiddenStyle, }, }, '@container': { [TYPE_NARROW]: { ...visuallyHiddenStyle, }, }, }, },});
// DS gap: Box has no container-query display. Each body groups one specimen and its five tokens.export const typeGroup = style({ '@layer': { components: { '@container': { [TYPE_NARROW]: { display: 'block', }, }, }, },});
// DS gap: Box has no per-side border props. The specimen is a clean full-width row before its// tokens; the top rule separates it from the preceding table group.export const typeSpecimen = style({ '@layer': { components: { borderBlockStart: `1px solid ${vars.color.border.decorative}`, paddingBlock: vars.space['12'], textAlign: 'start', '@container': { [TYPE_NARROW]: { display: 'block', paddingBlock: vars.space['16'], }, }, }, },});
// DS gap: Box has no container-query display prop. The specimen cell fills its stacked table row.export const typeSpecimenRow = style({ '@layer': { components: { '@container': { [TYPE_NARROW]: { display: 'block', }, }, }, },});
// DS gap: Box has no container-query display. A narrow token row stacks its token name above its// value.export const typeRow = style({ '@layer': { components: { '@container': { [TYPE_NARROW]: { borderBlockEnd: `1px solid ${vars.color.border.decorative}`, display: 'block', selectors: { '&:last-child': { borderBlockEnd: 'none', }, }, }, }, }, },});
// DS gap: Box has no per-side border props or verticalAlign prop. Each token row has one separator.export const typeCell = style({ '@layer': { components: { borderBlockEnd: `1px solid ${vars.color.border.decorative}`, scrollMarginBlockStart: `calc(${SITE_HEADER_BLOCK_SIZE} + ${vars.space['24']})`, textAlign: 'start', verticalAlign: 'middle', selectors: { 'tbody > tr:last-child > &': { borderBlockEnd: 'none', }, }, '@container': { [TYPE_NARROW]: { borderBlockEnd: 'none', display: 'block', }, }, }, },});
// DS gap: Box has no container-query padding. Token identification padding on narrow screens.export const typeNameCell = style({ '@layer': { components: { paddingBlock: vars.space['8'], '@container': { [TYPE_NARROW]: { paddingBlock: vars.space['12'], }, }, }, },});
// DS gap: Box has no container-query padding or white-space prop. The resolved value stays compact// on desktop and can wrap on narrow screens.export const typeValueCell = style({ '@layer': { components: { paddingBlock: vars.space['8'], whiteSpace: 'nowrap', '@container': { [TYPE_NARROW]: { paddingBlock: vars.space['12'], whiteSpace: 'normal', }, }, }, },});
/** The inline size of a motion sample's track. */export const MOTION_TRACK_SIZE = '6rem';/** The size of the dot that travels a motion sample's track. */export const MOTION_DOT_SIZE = vars.space['12'];
const motionDotTravelDistance = `calc(${MOTION_TRACK_SIZE} - ${MOTION_DOT_SIZE})`;
/** Motion-sample loop length used by both duration and easing keyframes. */const MOTION_SAMPLE_CYCLE = '1s';
const motionDotAtStart = { transform: 'translateX(0)',};
const motionDotAtEnd = { transform: `translateX(${motionDotTravelDistance})`,};
/** Duration samples: travel the full track each half-cycle; timing is set inline per token. */const motionDotTravelDuration = keyframes({ from: motionDotAtStart, to: motionDotAtEnd,});
/** Easing samples: ~30% travel each way so the curve is readable at a fixed cycle length. */const motionDotTravelEasing = keyframes({ '0%, 10%, 90%, 100%': motionDotAtStart, '40%, 60%': motionDotAtEnd,});
// DS gap: Box has no transform prop, and logical properties have no transform. The track runs// right to left in a right-to-left page.const motionDotAtEndRtl = { transform: `translateX(calc(-1 * ${motionDotTravelDistance}))`,};
const motionDotTravelDurationRtl = keyframes({ from: motionDotAtStart, to: motionDotAtEndRtl,});
const motionDotTravelEasingRtl = keyframes({ '0%, 10%, 90%, 100%': motionDotAtStart, '40%, 60%': motionDotAtEndRtl,});
// DS gap: Box has no animation props, and Luke UI ships no motion sample. Duration samples// alternate along the track with a per-token linear() timing function inside MOTION_SAMPLE_CYCLE.// Easing samples rest at each end, cross over ~30% of the cycle, and set animation-timing-function// inline. Each table toggles motion with data-animate and pauses offscreen.export const motionSamples = style({ '@layer': { components: { display: 'block', }, },});
export const motionDotDuration = style({ '@layer': { components: { animationDirection: 'alternate', animationDuration: MOTION_SAMPLE_CYCLE, animationIterationCount: 'infinite', animationName: motionDotTravelDuration, selectors: { ':where([dir="rtl"]) &': { animationName: motionDotTravelDurationRtl, }, }, }, },});
export const motionDotEasing = style({ '@layer': { components: { animationDuration: MOTION_SAMPLE_CYCLE, animationIterationCount: 'infinite', animationName: motionDotTravelEasing, selectors: { ':where([dir="rtl"]) &': { animationName: motionDotTravelEasingRtl, }, }, }, },});
globalStyle(`${motionSamples}[data-animate='false'] ${motionDotDuration}`, { animation: 'none',});
globalStyle(`${motionSamples}[data-animate='false'] ${motionDotEasing}`, { animation: 'none',});
globalStyle(`${motionSamples}:not([data-in-view='true']) ${motionDotDuration}`, { animationPlayState: 'paused',});
globalStyle(`${motionSamples}:not([data-in-view='true']) ${motionDotEasing}`, { animationPlayState: 'paused',});