Skip to content

CSS variables

Every --aparte-* variable aparté declares or reads — 469 in total: 415 declared in the stylesheet’s :root and 54 read by a component with a built-in default. Override any of them as shown in Theming. Both halves are swept from the source on every build.

A row marked palette only is declared but read by nothing in aparté: it is there for your own CSS to reference (--aparte-bg is the page background your app paints — core deliberately leaves the chat transparent), or it is a step of a scale nothing happens to use yet. Setting one changes nothing on its own.

VariableDefaultNotes
--aparte-primary#b07d33
--aparte-primary-hover#a06f28
--aparte-secondary#9a8fb0
--aparte-neutral#6d6479

--aparte-surface-3 (code blocks, headers) and --aparte-text-inverse are no longer literals here: both are derived from the masters in the anchored layer below, so the eight-line rebrand the theming guide documents actually covers them. They were the two palette hexes that survived a rebrand — a consumer on a blue brand kept the brass palette’s cream for its tooltip ink and its code headers.

VariableDefaultNotes
--aparte-bg#f6f2ea
--aparte-surface-1#fffdf8
--aparte-surface-2#f1ebdfBubbles, cards
VariableDefaultNotes
--aparte-text#241f2b
--aparte-text-muted#6d6479
VariableDefaultNotes
--aparte-border#e5ddce
--aparte-border-width1px
--aparte-border-focusvar(--aparte-primary)
--aparte-focus-outline-offsetvar(--aparte-space-1)THE focus ring: a 2px outline in this colour, one spacing step OUTSIDE the box, on every control. It used to be drawn two ways — this outline on seventeen recipes and a soft box-shadow wash (30 % of the accent, 1.39:1 against the page) on five — and the offset was −2px, inside the box, so on a bordered row the ring sat as a second concentric line and the next row painted over its edge.
--aparte-border-controlcolor-mix(in srgb, var(--aparte-text) 30%, var(--aparte-surface-1))The edge of a CONTROL — a field, a choice box, the select’s trigger. --aparte-border separates two regions and is right to be faint; a thing someone can touch has to be seen, and six previews showed a control with no visible edge. Derived from the ink and the ground so both schemes follow; the mix is a first setting.
VariableDefaultNotes
--aparte-info#3b82f6
--aparte-success#10b981
--aparte-warning#f59e0b
--aparte-error#ef4444

THE SAME FIVE, AS TEXT ON THE PAGE — and DERIVED, not written down

Section titled “THE SAME FIVE, AS TEXT ON THE PAGE — and DERIVED, not written down”

The five fills above are for a solid button, a badge, a wash. Read as text on --aparte-bg they measured 3.23 (primary), 3.29 (info), 2.27 (success), 1.92 (warning) and 3.37 (error) against the 4.5:1 AA asks of body text, and every ghost, outline and soft button, the tool-call status, the form error and every link core renders was reading the fill. These used to be ten hand-picked hexes — five here, five in the dark block — each measured against THIS repo’s --aparte-bg. Same defect the solid ink had: a consumer’s palette got a colour computed for someone else’s page. Each is now the accent with its own hue and chroma kept and its LIGHTNESS forced to --aparte-ink-l, which is the one value that has to flip with the theme. Why forced rather than mixed: pulling the accent toward --aparte-text reads well and fails on an accent already close to the background — measured 3.41 on a navy brand primary, which cannot be rescued by moving toward the text since it has to move PAST it. Setting the lightness outright has no such blind spot. Measured across 80 combinations — 5 intents x 4 palettes (ours light, ours dark, and two invented) x 4 grounds (bg, surface-1/2/3) — the worst case is 4.80.

VariableDefaultNotes
--aparte-ink-l0.40The target lightness itself is a literal and stays here; the five inks that read it are derived, so they live on the anchored layer below.

Every step of every scale is COMPUTED from these (see the anchored block below), so moving one moves the whole family. That is the property a scale written out as literals cannot have. Spacing and radius are px, type is rem, and the split is deliberate: rem type follows the reader’s browser font size, which is the one accessibility setting a chat has to honour. Compounding that into every gap as well makes a large-type page grow twice over. Wanting rem spacing anyway is now a one-line change here — which is the point of having a master at all.

VariableDefaultNotes
--aparte-space-unit2px
--aparte-radius-unit3px
--aparte-font-scale1.08
--aparte-radius-full9999pxNot a step of the radius scale: a pill is a shape, not a size.

Font-weight scale — every font-weight references these (no raw weights)

Section titled “Font-weight scale — every font-weight references these (no raw weights)”
VariableDefaultNotes
--aparte-font-weight-normal400
--aparte-font-weight-medium500
--aparte-font-weight-semibold600
--aparte-font-weight-bold700palette only

Line-height scale — every line-height references these (no raw values)

Section titled “Line-height scale — every line-height references these (no raw values)”

WHERE THE INK ON A COLOURED FILL COMES FROM, and why there is no literal here. --aparte-btn-on-intent / --aparte-badge-on-intent / --aparte-checkbox-on-intent are DERIVED from their own fill, in a @supports block in each recipe — see the long note in button.css. --aparte-on-intent below survives only as the fallback for a browser without relative colour syntax. THE PAIR IS THE CONTRACT. Every intent has two names — the fill and the ink that goes on it — and a theme may declare either half: —aparte-primary / —aparte-on-primary —aparte-secondary / —aparte-on-secondary —aparte-neutral / —aparte-on-neutral —aparte-info / —aparte-on-info —aparte-success / —aparte-on-success —aparte-warning / —aparte-on-warning —aparte-error / —aparte-on-error None of the seven --aparte-on-* is declared here, and that is the point: an undeclared partner means “core, work it out”, and the recipes derive it from the fill. Declare one and it wins, everywhere that intent is used. So a theme author decides where they have an opinion and stays silent where they do not, and nothing is guessed behind their back — which is what the old single frozen constant did. This is the shape shadcn uses (--primary / --primary-foreground), with Bootstrap’s computed default behind it. Before, one hex served all seven and only neutral had an escape, hardcoded.

VariableDefaultNotes
--aparte-line-height-none1
--aparte-line-height-tight1.25
--aparte-line-height-normal1.5
--aparte-line-height-relaxed1.6
--aparte-line-height-loose1.7
--aparte-font-familysystem-ui, -apple-system, sans-serifTypography
--aparte-ink-flip0.57The lightness at which the derived ink flips from light to dark. One knob, read by all three recipes, so the threshold is tunable in one place instead of three. 0.57 sits in a wide gap: on this palette the only fill wanting light ink measures 0.519 and the lowest wanting dark measures 0.623.
--aparte-ink-dark0.176And how dark the dark end goes. NOT 0: at a lightness of zero OKLCH ignores chroma entirely, so the ink came out pure #000000 on every fill and the hue this derivation carries was thrown away — the first version of it claimed a warm black on brass and shipped a flat one. 0.176 is the lightness of #14100a, the ink that was hand-picked here before, so the brass keeps the warm near-black it had and a blue fill gets a cool one, each from its own hue.
--aparte-on-intent#14100aFALLBACK ONLY — a browser without relative colour syntax (Firefox before 128). Everywhere else the ink is derived from the fill and this value is never read. It is a hex chosen by measuring against THIS repo’s own five fills, which is exactly why it stopped being the default path: it made core’s rendering depend on core’s palette, in a library whose consumers bring their own. Our theming guide teaches an eight-line rebrand and a one-attribute <aparte-chat style="--aparte-primary: …">, and names this token in neither — so a consumer with a dark brand primary got near-black on it and no signal (1.11:1 measured on #1a1a2e). Kept as the fallback because a degraded old browser is better than an unstyled one; it is not the contract. It does not flip with the theme, and in a themed system that anomaly was the tell I wrote a paragraph justifying instead of reading.
--aparte-focus-outline-width2pxOutline width for keyboard focus + drag-over indicators.
--aparte-scrollbar-width6pxScrollbars rendered by the library.
--aparte-scrollbar-tracktransparentThe transcript’s scrollbar track; the thumb is derived from the neutral, in the derived layer. A host page with a scrollbar of its own (the docs site has one) sets both so the chat’s does not read as a second, foreign scrollbar.
--aparte-touch-target-size44pxComfortable hit-area on coarse (touch) pointers.
--aparte-message-max-width800pxMessages
--aparte-message-content-bg-assistanttransparentBubbles. The user’s bubble tint is DERIVED from the accent in the anchored layer below (it was a frozen plum hex that outlived every rebrand); the assistant is plain prose (AI-chat convention) — flip it to get a two-bubble chat-app look.

Aliases used by some segment renderers (artifact card, tool pill)

Section titled “Aliases used by some segment renderers (artifact card, tool pill)”

Mapped to existing tokens so consumers that override —aparte-surface-1/2 get cohesive results without touching every rule.

VariableDefaultNotes
--aparte-code-font-familyui-monospace, 'Cascadia Code', 'JetBrains Mono', 'Geist Mono', SFMono-Regular, Consolas, 'Liberation Mono', monospaceCode typography — exposed so apps can swap in Geist Mono / Cascadia / …
VariableDefaultNotes
--aparte-attachment-image-size72px
--aparte-attachments-max-height140px
--aparte-attachment-chip-bgvar(--aparte-surface-2)
--aparte-attachment-chip-bordervar(--aparte-border)
--aparte-attachment-chip-radiusvar(--aparte-radius-sm)palette only
--aparte-attachment-chip-colorvar(--aparte-text)palette only

Attachment thumbnail tile (composer preview + sent-message strip)

Section titled “Attachment thumbnail tile (composer preview + sent-message strip)”
VariableDefaultNotes
--aparte-thumb-name-color#ffffff
--aparte-thumb-name-scrimlinear-gradient(to top, rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0))
--aparte-thumb-name-padding14px 5px 4px
--aparte-thumb-remove-size18px
--aparte-thumb-remove-inset3px
--aparte-thumb-remove-bgrgba(0, 0, 0, 0.6)
--aparte-thumb-remove-bg-hoverrgba(0, 0, 0, 0.85)
--aparte-thumb-remove-color#ffffff
--aparte-thumb-radiusvar(--aparte-radius-lg)

How much of its intent a marked row’s ground takes, and how wide the bar on its start edge is. One pair for the select’s chosen option, the checked field choice, the active conversation and an answered question.

VariableDefaultNotes
--aparte-mark-tint18%
--aparte-mark-bar2px
--aparte-branch-picker-label-min-width32pxBranch Picker
--aparte-avatar-size32pxAvatars

Fractions of the size, computed on the element (display/avatar.css)

Section titled “Fractions of the size, computed on the element (display/avatar.css)”

The initials’ type, the corner and the group’s overlap follow the ramp instead of sitting at one absolute value for 20px and 56px alike.

VariableDefaultNotes
--aparte-avatar-initials-ratio0.34
--aparte-avatar-radius-ratio0.28
--aparte-avatar-overlap-ratio0.28
--aparte-avatar-image-usernone
--aparte-avatar-image-assistantnone
--aparte-avatar-image-size90%
VariableDefaultNotes
--aparte-input-editor-max-height200pxInput
--aparte-name-colorvar(--aparte-text)
--aparte-timestamp-colorvar(--aparte-text-muted)
--aparte-content-colorvar(--aparte-text)

One knob, inherited — a container declares --aparte-icon-size and every glyph below follows. The steps are on the type scale, in the derived layer. Send button + inline action buttons (their size is the lg step, in the derived layer)

VariableDefaultNotes
--aparte-input-action-btn-icon-size20px
--aparte-model-select-min-width120px
VariableDefaultNotes
--aparte-status-bgtransparentpalette only
--aparte-status-dot-size6px
--aparte-pulse-floor0.55The floor of every pulsing dot’s opacity: above the perception threshold.
--aparte-waiting-height1.5emWaiting Indicator (the bubble’s own dots, before the first token)
--aparte-status-colorvar(--aparte-text-muted)
VariableDefaultNotes
--aparte-thinking-content-max-height300px
--aparte-thinking-content-line-height1.55
--aparte-thinking-rail-width2px
--aparte-thinking-bordervar(--aparte-border)
--aparte-thinking-bgvar(--aparte-surface-2)palette only
--aparte-thinking-textvar(--aparte-text-muted)
--aparte-thinking-contentvar(--aparte-text)
--aparte-thinking-content-bgvar(--aparte-surface-1)palette only

Elicitation — the panel a tool’s question is asked in

Section titled “Elicitation — the panel a tool’s question is asked in”

These exist because the panel’s CSS used to be a <style> injected from JS with every value hardcoded, so it was the one surface in the library that could not be themed and did not appear in the generated CSS reference.

VariableDefaultNotes
--aparte-elic-max-height50vh
--aparte-elic-control-size15px
--aparte-elic-textarea-min-height64pxThe tool-call row’s corner. Declared here rather than left as an inline fallback in the renderer: an undeclared custom property still WORKS, which is what makes it dangerous — it is absent from the generated CSS reference, so a consumer has no way to discover the knob exists.
--aparte-elic-step-underline2pxTabs, not pills. Two passes to get here, both wrong for the same reason — decorating a control instead of choosing what it IS. First a fully-round pill filled with the primary colour: the only 9999px thing in a panel built from 4/6/8px corners, and it read as a second send button. Then a small bordered rectangle: still a button. A step indicator is a TAB — text on a rail, and the current one carries a thick underline. No box, no fill, no border.
--aparte-elic-dismiss-room72pxSpace kept clear at the end of the tab rail for the corner escape.
VariableDefaultNotes
--aparte-code-copy-hover-bgrgba(0, 0, 0, 0.05)
--aparte-code-copy-icon-size14px
--aparte-code-inline-font-size0.875em
--aparte-scroll-btn-shadow0 2px 8px rgba(0, 0, 0, 0.12)Scroll-to-bottom button (its size is the lg step, in the derived layer)
--aparte-code-bgvar(--aparte-surface-1)
--aparte-code-header-bgvar(--aparte-surface-2)
--aparte-code-bordervar(--aparte-border)
--aparte-code-header-textvar(--aparte-text-muted)
VariableDefaultNotes
--aparte-error-bg#fef2f2palette only
--aparte-error-border#fecaca
--aparte-error-solid#dc2626
--aparte-error-text#991b1b
--aparte-error-rail-width4px

Heading and block margins, in em so they scale with the heading’s own size — the one place em beats the px scale. The family already named its sizes and weights; these were the only members still written out in the rules.

VariableDefaultNotes
--aparte-prose-p-margin0.4em 0
--aparte-prose-h1-size1.4em
--aparte-prose-h2-size1.25em
--aparte-prose-h3-size1.1em
--aparte-prose-list-indent1.5em
--aparte-prose-list-margin0.4em 0
--aparte-prose-li-margin0.15em 0
--aparte-prose-h1-margin0.6em 0 0.2em
--aparte-prose-h2-margin0.5em 0 0.2em
--aparte-prose-h3-margin0.4em 0 0.15em
--aparte-prose-h4-margin0.3em 0 0.1em
--aparte-prose-blockquote-margin0.4em 0
--aparte-prose-blockquote-indent1em
--aparte-prose-hr-margin0.75em 0
--aparte-prose-code-padding0.1em 0.3em
--aparte-prose-blockquote-border-width3px
--aparte-prose-blockquote-bordervar(--aparte-border)
--aparte-prose-code-bgvar(--aparte-surface-2)

Breathing room UNDER the composer, so it does not sit flush against the bottom edge of a full-height chat. Same 16px the viewport already puts between the last bubble and the composer — this restores the symmetry rather than inventing a value. Set it to 0 for a flush composer (a full-bleed mobile shell with a docked keyboard is the legitimate case).

VariableDefaultNotes
--aparte-chat-bottom-gapvar(--aparte-space-8)
VariableDefaultNotes
--aparte-input-line-heightvar(--aparte-line-height-normal)
--aparte-sidebar-bgvar(--aparte-surface-2)Shell
--aparte-sidebar-insetvar(--aparte-space-3)The one inline inset of the column’s four regions — header, search, body, footer. They used to carry three (16 / 12 / 12 / 16) and the content sat on two axes.

Scroll rail — the ticks beside the transcript

Section titled “Scroll rail — the ticks beside the transcript”

Off-scale on purpose: a 2px line and a 14px mark are a minimap’s measures, not spacing steps.

VariableDefaultNotes
--aparte-scroll-rail-tick-size14px
--aparte-scroll-rail-tick-thickness2px
--aparte-scroll-rail-hit-size24pxWhat a tick is to a POINTER, as opposed to what it is to the eye: WCAG 2.5.8’s 24px minimum. The drawn line stays 14×2 — this sizes the pseudo-element around it, and the gap below derives from it so the pitch rises with the zone. The rail is display: none under (pointer: coarse), so 2.5.8’s 24px is the bar here and not 2.5.5’s 44px.
--aparte-scroll-rail-share.6The share of the transcript’s height the rail may take: a list of ticks, not a minimap, so it is the height of its list up to this, centred, and scrolls past it.
--aparte-context-ring-size22pxContext gauge, ring variant — a diameter, and a stroke in the ring’s own 36-unit box. Literals like the rail’s: a 22px ring is a toolbar’s measure, not a spacing step.

Shell — the sidebar’s column, the header’s row and the split’s seam

Section titled “Shell — the sidebar’s column, the header’s row and the split’s seam”
VariableDefaultNotes
--aparte-sidebar-width260px
--aparte-app-header-height48px
--aparte-scrimrgba(0, 0, 0, 0.35)
--aparte-split-position38%The split’s primary pane and the bounds it moves between. Any length or percentage: the browser clamps them in the grid template, so nothing in JS parses a unit. The seam is 4px because that is a seam; the grab zone around it is 12px because that is a target, and it grows to the touch target on a coarse pointer (responsive.css).

At 320px this floor was wider than a phone, so the primary pane took everything and the end pane was annihilated to 0.

VariableDefaultNotes
--aparte-split-minmin(20rem, 100%)
--aparte-split-max60%

The seam’s TRACK — the grab zone, and what the grid reserves

Section titled “The seam’s TRACK — the grab zone, and what the grid reserves”

The painted line inside it is --aparte-split-seam-width (derived layer): one token used to size both, so the line could not be thinned without moving the layout, and at 4px it was four times the kit’s rule with no grip — an interaction drawn as a decoration.

VariableDefaultNotes
--aparte-split-handle-size12px
--aparte-split-hit-area12px

Each is off-scale on purpose — a 40px file tile is a tile, not fourteen spacing units — so each gets a name rather than sitting as a magic number in a rule. Literal, like every other size token here (--aparte-send-btn-size, --aparte-avatar-size): deriving the control sizes from —aparte-space-unit is a separate decision, not a side effect of naming them.

Four durations because the sheet actually uses four — 0.12s and 0.15s were doing the same job on the same properties, which is drift; both are named here rather than collapsed, so the choice is visible and stays yours. spin and pulse are loop lengths, not state changes.

VariableDefaultNotes
--aparte-duration-fast0.12s
--aparte-duration-base0.15s
--aparte-duration-slow0.2s
--aparte-duration-slower0.3s
--aparte-duration-spin0.7s
--aparte-duration-pulse1.2s
--aparte-easeease
--aparte-slide-distance8pxHow far a panel travels as it appears.

Two names because the sheet has exactly two stacked things: the dismiss control that must clear its own row, and the scroll button floating over the transcript. No invented elevation scale — a host that needs its modal above the button raises it here.

VariableDefaultNotes
--aparte-z-raised1
--aparte-z-dropdown1000
--aparte-z-floating10
--aparte-line-height-snug1.4Select. Its dropdown shadow used to live as a fallback inside select.css, which meant it had no dark value at all — rgba(0,0,0,0.1) over a dark surface is no shadow. Declared here, it themes like every other.
--aparte-disabled-opacity0.5One value for “this control is disabled”. It was seven states carrying five different opacities — 0.3, 0.45, 0.5, 0.5, 0.55, 0.6, 0.6 — and not one of them had a comment saying why, so there was nothing to preserve in keeping them. Deliberately ONE knob and not Material’s content/container pair: that split tints a container’s background apart from its text, and every case here is a whole control fading. Not Bootstrap’s per-component variable either — that is the drift we just removed, with names on it.

Literal, and each has a dark value below — a shadow tuned for a light ground is no shadow at all on a dark one, which is how the select’s dropdown lost its lift in dark mode until it was caught.

VariableDefaultNotes
--aparte-menu-shadow0 4px 12px rgba(0, 0, 0, 0.1)
--aparte-popover-shadow0 1px 3px rgba(0, 0, 0, 0.08), 0 10px 28px rgba(0, 0, 0, 0.16)
--aparte-tooltip-shadow0 2px 8px rgba(0, 0, 0, 0.12)

The composer laid over the transcript (overlay-composer)

Section titled “The composer laid over the transcript (overlay-composer)”

A thing that floats has to be seen floating, and its ground is the transcript’s own.

VariableDefaultNotes
--aparte-composer-overlay-shadow0 -4px 16px rgba(0, 0, 0, 0.06)
--aparte-switch-thumb-shadow0 1px 2px rgba(0, 0, 0, 0.15)
--aparte-range-thumb-shadow0 1px 2px rgba(0, 0, 0, 0.15)
--aparte-select-shadow0 1px 3px rgba(0, 0, 0, 0.08), 0 10px 28px rgba(0, 0, 0, 0.16)
--aparte-select-min-width200px
--aparte-select-dropdown-max-height300px
--aparte-select-status-dot-size8px
--aparte-select-spinner-size12px

Literal like every other control size: tightening the spacing scale must not shrink a button. The three are the three this library already uses — a conversation action, an action-bar button, a send button.

Literal like every other control size: tightening the spacing scale must not shrink an avatar or a spinner.

Sizes for the five native controls the field sheet dresses

Section titled “Sizes for the five native controls the field sheet dresses”

Literal, like every other control size. The date minimum width exists because a date field otherwise collapses to the intrinsic width of its digits, which differs per locale.

VariableDefaultNotes
--aparte-color-size32px
--aparte-datetime-min-width160px
--aparte-meter-height8px
--aparte-datetime-indicator-opacity0.6
--aparte-thumbnail-size56px
--aparte-thumbnail-size-sm40px
--aparte-thumbnail-size-lg72px
--aparte-avatar-size-xs20px
--aparte-avatar-size-sm24px
--aparte-avatar-size-lg40px
--aparte-avatar-size-xl56px
--aparte-spinner-size-sm12px
--aparte-spinner-size-md16px
--aparte-spinner-size-lg24px
--aparte-skeleton-rect-height5rem
--aparte-skeleton-text-last-width60%
--aparte-progress-indeterminate-width40%

Four steps, and EVERY control reads one: a text button used to have no height (23px of padding and line-height) beside 24, 29, 32, 36 and 44px neighbours, and the 36px family (send, the input’s action button, the scroll button) lived off the scale as literals. sm is the small control (action bar, branch arrows, a row’s ⋯), md the default button, lg the field and the composer’s controls at rest, xl the large button.

VariableDefaultNotes
--aparte-btn-size-sm24px
--aparte-btn-size-md32px
--aparte-btn-size-lg36px
--aparte-btn-size-xl40px

The field family’s literal sizes, beside the button’s

Section titled “The field family’s literal sizes, beside the button’s”

The derived knobs (paddings, radius) sit in the anchored layer below, like the button’s own.

VariableDefaultNotes
--aparte-field-textarea-min-height80px
--aparte-checkbox-size18px
--aparte-radio-size18px
--aparte-radio-dot-size8px
--aparte-switch-width40px
--aparte-switch-height22px
--aparte-switch-thumb-inset2px
--aparte-range-thumb-size16px
--aparte-range-track-height4px
--aparte-tool-spinner-size10px
--aparte-model-select-chevron-room28px

The five accents AS TEXT, each derived from its own fill

Section titled “The five accents AS TEXT, each derived from its own fill”

See the long note in the literal block. Anchored, not declared once on :root, for the reason this whole layer exists: an <aparte-chat> that sets its own --aparte-primary only gets a matching ink if the ink is declared on that element too.

VariableDefaultNotes
--aparte-primary-inkoklch(from var(--aparte-primary) var(--aparte-ink-l) c h)
--aparte-info-inkoklch(from var(--aparte-info) var(--aparte-ink-l) c h)
--aparte-success-inkoklch(from var(--aparte-success) var(--aparte-ink-l) c h)
--aparte-warning-inkoklch(from var(--aparte-warning) var(--aparte-ink-l) c h)
--aparte-error-inkoklch(from var(--aparte-error) var(--aparte-ink-l) c h)

The scales, computed from the masters above

Section titled “The scales, computed from the masters above”

They live in the ANCHORED block and not with the literals because a value that reads a var is substituted where it is DECLARED: an <aparte-chat> that sets its own —aparte-space-unit only follows it if the steps are declared on that element too. scripts/check-derived-vars.mjs holds that rule. Rem, so the reader’s browser font size is honoured; times the scale, so an app can size the whole chat down or up without restating six values.

VariableDefaultNotes
--aparte-space-1calc(var(--aparte-space-unit) * 1)
--aparte-space-2calc(var(--aparte-space-unit) * 2)
--aparte-space-3calc(var(--aparte-space-unit) * 3)
--aparte-space-4calc(var(--aparte-space-unit) * 4)
--aparte-space-5calc(var(--aparte-space-unit) * 5)
--aparte-space-6calc(var(--aparte-space-unit) * 6)
--aparte-space-7calc(var(--aparte-space-unit) * 7)
--aparte-space-8calc(var(--aparte-space-unit) * 8)
--aparte-radius-xscalc(var(--aparte-radius-unit) * 1)
--aparte-radius-smcalc(var(--aparte-radius-unit) * 2)
--aparte-radius-mdcalc(var(--aparte-radius-unit) * 3)
--aparte-radius-lgcalc(var(--aparte-radius-unit) * 4)
--aparte-radius-xlcalc(var(--aparte-radius-unit) * 6)
--aparte-font-size-2xscalc(0.625rem * var(--aparte-font-scale))
--aparte-font-size-xscalc(0.6875rem * var(--aparte-font-scale))
--aparte-font-size-smcalc(0.75rem * var(--aparte-font-scale))
--aparte-font-size-mdcalc(0.8125rem * var(--aparte-font-scale))
--aparte-font-size-basecalc(0.9375rem * var(--aparte-font-scale))

Above the body, in the order the names say

Section titled “Above the body, in the order the names say”

lg used to be 0.875rem — SMALLER than base — with nothing above it, so a title and a placeholder shared a size and a card’s title was smaller than its body. A scale whose name lies is worse than a short one.

VariableDefaultNotes
--aparte-font-size-lgcalc(1.0625rem * var(--aparte-font-scale))
--aparte-font-size-xlcalc(1.25rem * var(--aparte-font-scale))palette only
--aparte-font-size-2xlcalc(1.5rem * var(--aparte-font-scale))palette only

Elicitation and the conversation item, on the scale

Section titled “Elicitation and the conversation item, on the scale”

They were literals off every step — 0.76 / 0.78 / 0.8 / 0.82rem and a 7px 10px padding written twice — so two panels sat outside the type and spacing systems and neither followed —aparte-font-scale. Each lands on its nearest step; the largest move is under half a pixel. Derived, so they live here. Split, because the action bar’s offset below a message is computed from the vertical half (#56) — a shorthand cannot be read by parts.

VariableDefaultNotes
--aparte-message-gapvar(--aparte-space-6)The component tier, on the scales. These were literals whose values already WERE steps — --aparte-message-gap was 12px, which is space-6 — so a master reached the scale and stopped there: a chat at —aparte-space-unit:3px grew its gaps and kept its message padding at 16px. The line drawn here, and it is a line: the spacing scale governs gutters, padding and margin; the radius scale governs corners; the type scale governs text. None of them governs a STROKE width or a CONTROL size. —aparte-thinking-rail-width stays 2px because loosening spacing must not thicken a rule, and —aparte-avatar-size stays 32px because tightening it must not shrink an avatar. The eight text sizes were px and are rem now, like the rest of the typography: identical at a 16px root, and following the reader’s setting elsewhere.
--aparte-message-padding-blockvar(--aparte-space-8)
--aparte-message-padding-inlinevar(--aparte-space-6)
--aparte-message-content-paddingvar(--aparte-space-5) var(--aparte-space-7)
--aparte-action-bar-gapvar(--aparte-space-2)
--aparte-branch-picker-gapvar(--aparte-space-2)
--aparte-input-gapvar(--aparte-space-4)
--aparte-waiting-dot-gapvar(--aparte-space-2)
--aparte-elic-gapvar(--aparte-space-3)
--aparte-elic-paddingvar(--aparte-space-3) var(--aparte-space-2)
--aparte-elic-field-gapvar(--aparte-space-4)
--aparte-elic-step-paddingvar(--aparte-space-2) var(--aparte-space-1)
--aparte-elic-step-gapvar(--aparte-space-7)
--aparte-viewport-paddingvar(--aparte-space-8)
--aparte-scrollbar-thumbvar(--aparte-neutral)
--aparte-input-padding-xvar(--aparte-space-6)

Half of what the control size leaves once one line of its own text is taken out. It used to be a flat 10px, which made the editor 44px beside 36px buttons and, the row aligning at the end, sat the send button 4px below the editor’s centre. Derived from the editor’s own line (1lh), so a coarse pointer’s 44 and a changed font both keep the editor and its buttons on one centre.

VariableDefaultNotes
--aparte-input-padding-ycalc((var(--aparte-composer-control-size) - 1lh) / 2)
--aparte-conv-list-gapvar(--aparte-space-1)
--aparte-conv-item-gapvar(--aparte-space-3)
--aparte-elic-option-radiusvar(--aparte-radius-md)
--aparte-elic-input-radiusvar(--aparte-radius-md)
--aparte-tool-row-radiusvar(--aparte-radius-md)

The split’s painted seam: the kit’s rule, inside a 12px track

Section titled “The split’s painted seam: the kit’s rule, inside a 12px track”
VariableDefaultNotes
--aparte-split-seam-widthvar(--aparte-border-width)
--aparte-name-font-sizevar(--aparte-font-size-md)
--aparte-timestamp-font-sizevar(--aparte-font-size-sm)
--aparte-branch-picker-label-sizevar(--aparte-font-size-sm)
--aparte-input-font-sizevar(--aparte-font-size-base)
--aparte-content-font-sizevar(--aparte-font-size-base)
--aparte-input-editor-font-sizevar(--aparte-font-size-base)
--aparte-status-font-sizevar(--aparte-font-size-md)

Derived, so a chat that moves —aparte-space-unit or —aparte-font-scale takes its buttons with it. Defaults are what the 33 control rules already measured agree on: a muted ghost that fills on hover.

Every one composed from a scale or the palette, so a chat that moves —aparte-space-unit or —aparte-font-scale carries them along.

Dark on a light theme, light on a dark one. Reading the text pair gives exactly that, and for free — no third palette.

The distance between a placed tooltip (data-side) and its anchor

Section titled “The distance between a placed tooltip (data-side) and its anchor”

The arrow’s half-diagonal, so the tip lands on the trigger rather than 2px short of it. A meter is judged against a range, so its three bands take the three status colours rather than one accent.

VariableDefaultNotes
--aparte-tooltip-gapvar(--aparte-space-3)
--aparte-tooltip-bgvar(--aparte-text)
--aparte-tooltip-colorvar(--aparte-text-inverse)
--aparte-spinner-sizevar(--aparte-spinner-size-md)Shared by both spinners the library ships — the display recipe and the <aparte-progress-spinner> element. Declared here rather than on one of them, so the other can resolve it too.

The whole line is the target, so it carries the padding a control alone would not.

VariableDefaultNotes
--aparte-field-choice-gapvar(--aparte-space-5)
--aparte-field-choice-paddingvar(--aparte-space-4) var(--aparte-space-5)
--aparte-field-choice-radiusvar(--aparte-radius-lg)
--aparte-meter-trackvar(--aparte-surface-3)
--aparte-meter-optimumvar(--aparte-success)
--aparte-meter-suboptimumvar(--aparte-warning)
--aparte-meter-poorvar(--aparte-error)
--aparte-output-paddingvar(--aparte-space-1) var(--aparte-space-2)
--aparte-output-bgvar(--aparte-surface-2)
--aparte-link-colorvar(--aparte-primary-ink)
--aparte-link-color-hovervar(--aparte-primary-hover)
--aparte-link-underline-thicknessvar(--aparte-border-width)
--aparte-link-underline-offsetvar(--aparte-space-1)
--aparte-thumbnail-bgvar(--aparte-surface-2)
--aparte-thumbnail-bordervar(--aparte-border)
--aparte-thumbnail-radiusvar(--aparte-radius-lg)
--aparte-alert-gapvar(--aparte-space-3)
--aparte-alert-paddingvar(--aparte-space-4) var(--aparte-space-5)
--aparte-alert-radiusvar(--aparte-radius-md)
--aparte-alert-rail-widthvar(--aparte-space-1)

The icon scale, in rem times the type factor

Section titled “The icon scale, in rem times the type factor”

The one scale in the theme that was pinned in px while every type size followed --aparte-font-scale, so a glyph beside text shrank optically the moment a reader enlarged the text. The loose default is 16 (it was 14, and its documentation already said 16).

VariableDefaultNotes
--aparte-icon-sizecalc(1rem * var(--aparte-font-scale))
--aparte-icon-size-smcalc(0.75rem * var(--aparte-font-scale))
--aparte-icon-size-lgcalc(1.125rem * var(--aparte-font-scale))
--aparte-icon-size-xlcalc(1.25rem * var(--aparte-font-scale))
--aparte-alert-icon-sizevar(--aparte-space-8)
--aparte-alert-dismiss-sizevar(--aparte-btn-size-sm)
--aparte-alert-title-font-sizevar(--aparte-font-size-md)
--aparte-alert-message-font-sizevar(--aparte-font-size-sm)
--aparte-avatar-fallback-bgvar(--aparte-surface-3)
--aparte-avatar-fallback-colorvar(--aparte-text)
--aparte-avatar-group-ring-widthcalc(var(--aparte-border-width) * 2)
--aparte-avatar-group-ring-colorvar(--aparte-surface-1)
--aparte-badge-font-sizevar(--aparte-font-size-2xs)
--aparte-badge-gapvar(--aparte-space-1)
--aparte-badge-paddingvar(--aparte-space-1) var(--aparte-space-2)
--aparte-badge-radiusvar(--aparte-radius-full)
--aparte-badge-min-widthvar(--aparte-space-8)
--aparte-badge-dot-sizevar(--aparte-space-4)
--aparte-tag-font-sizevar(--aparte-font-size-sm)
--aparte-tag-gapvar(--aparte-space-2)
--aparte-tag-paddingvar(--aparte-space-1) var(--aparte-space-3)
--aparte-tag-radiusvar(--aparte-radius-sm)
--aparte-tag-remove-sizevar(--aparte-space-6)
--aparte-tag-remove-icon-sizevar(--aparte-space-5)
--aparte-card-radiusvar(--aparte-radius-xl)
--aparte-card-header-paddingvar(--aparte-space-4) var(--aparte-space-5)
--aparte-card-body-paddingvar(--aparte-space-5)
--aparte-card-footer-paddingvar(--aparte-space-4) var(--aparte-space-5)
--aparte-card-header-font-sizevar(--aparte-font-size-base)
--aparte-divider-marginvar(--aparte-space-5)
--aparte-divider-label-gapvar(--aparte-space-4)
--aparte-divider-label-font-sizevar(--aparte-font-size-sm)
--aparte-kbd-bgvar(--aparte-surface-2)
--aparte-kbd-radiusvar(--aparte-radius-sm)
--aparte-kbd-paddingvar(--aparte-space-1) var(--aparte-space-2)
--aparte-kbd-font-sizevar(--aparte-font-size-2xs)
--aparte-kbd-min-widthvar(--aparte-space-8)
--aparte-kbd-border-bottom-widthvar(--aparte-space-1)
--aparte-progress-heightvar(--aparte-space-2)
--aparte-progress-fillvar(--aparte-primary)

The thing a value is read against, so it has to be seen. Derived RELATIVE to the page, not a surface level: a level sat at two different distances from the ground in light and dark.

VariableDefaultNotes
--aparte-trackcolor-mix(in srgb, var(--aparte-text) 8%, var(--aparte-bg))
--aparte-skeleton-basevar(--aparte-track)
--aparte-scroll-rail-widthmax(var(--aparte-scroll-rail-hit-size), calc(var(--aparte-scroll-rail-tick-size) * 1.6))The rail is as wide as its widest tick (the current one is 1.6× the resting size); it used to be a 20px literal clipping a 22.4px tick. The floor is one tick’s pressable zone: the rail clips, so a column narrower than the zone would cut the target back below 24px on the very edge a reader aims at.

The distance between two ticks IS the zone minus the line

Section titled “The distance between two ticks IS the zone minus the line”

Gap + thickness = the 24px pitch, so the zones tile edge to edge. Growing the zone alone would have overlapped them by 14px and left the z-order to decide every press.

VariableDefaultNotes
--aparte-scroll-rail-gapcalc(var(--aparte-scroll-rail-hit-size) - var(--aparte-scroll-rail-tick-thickness))
--aparte-skeleton-shinevar(--aparte-surface-3)

As tall as the content type, and a gap that is the rest of the content line, so the text that replaces it lands where it was (T21). It was 12px on an 18px step for a 16px line on a 27px step — a third of a line of jump per line.

VariableDefaultNotes
--aparte-skeleton-text-heightvar(--aparte-content-font-size)
--aparte-skeleton-text-gapcalc(var(--aparte-content-font-size) * (var(--aparte-content-line-height) - 1))
--aparte-skeleton-circle-sizevar(--aparte-avatar-size)
--aparte-spinner-thicknessvar(--aparte-space-1)

The field family’s knobs, beside the button’s

Section titled “The field family’s knobs, beside the button’s”

They used to be declared on .aparte-field itself, which is why .aparte-field-group (their PARENT) and .aparte-color (a sibling recipe) read a property that did not exist there and rendered SQUARE: a custom property is substituted where it is declared and only inherits downwards. Same reason they belong here rather than there: the theming guide sends a consumer to :root, and every other family’s knobs are here. The channels stay local on purpose (--aparte-checkbox-intent and friends are filled by a modifier, not set by a theme). The field’s resting height is the lg step of the control scale, one step above the default button and level with the composer’s controls; --sm and --lg sit one step either side. It used to be padding plus line-height, ~29px, the second tallest thing in a row of five different heights.

VariableDefaultNotes
--aparte-field-paddingvar(--aparte-space-2) var(--aparte-space-3)
--aparte-field-padding-smvar(--aparte-space-1) var(--aparte-space-2)
--aparte-field-padding-lgvar(--aparte-space-3) var(--aparte-space-4)
--aparte-field-radiusvar(--aparte-radius-md)
--aparte-field-sizevar(--aparte-btn-size-lg)
--aparte-field-size-smvar(--aparte-btn-size-md)
--aparte-field-size-lgvar(--aparte-btn-size-xl)

The 36px family, on the scale: named, not literal

Section titled “The 36px family, on the scale: named, not literal”
VariableDefaultNotes
--aparte-send-btn-sizevar(--aparte-btn-size-lg)
--aparte-input-action-btn-sizevar(--aparte-btn-size-lg)
--aparte-scroll-btn-sizevar(--aparte-btn-size-lg)
--aparte-btn-gapvar(--aparte-space-2)
--aparte-btn-paddingvar(--aparte-space-2) var(--aparte-space-3)
--aparte-btn-radiusvar(--aparte-radius-md)

One step lower was the origin of the “poor” look: 13px labels in 32px boxes, beside 15px prose.

VariableDefaultNotes
--aparte-btn-font-sizevar(--aparte-font-size-md)
--aparte-btn-bg-hovervar(--aparte-surface-2)
--aparte-btn-bg-activevar(--aparte-surface-3)
--aparte-btn-bg-toggledvar(--aparte-surface-2)
--aparte-btn-color-toggledvar(--aparte-text)
--aparte-btn-sizevar(--aparte-btn-size-md)
--aparte-btn-inkvar(--aparte-on-intent)
--aparte-btn-icon-sizevar(--aparte-icon-size-lg)
--aparte-btn-padding-smvar(--aparte-space-1) var(--aparte-space-2)
--aparte-btn-padding-lgvar(--aparte-space-3) var(--aparte-space-5)

The send button, the input’s action buttons, the editor’s single-line height: the lg step at rest, the touch target under a coarse pointer (responsive.css). It used to be undeclared, with 44px written as a fallback at every reader, so a desktop composer was a 62px bar around 15px of text and the coarse-pointer block had nothing left to enlarge.

VariableDefaultNotes
--aparte-composer-control-sizevar(--aparte-btn-size-lg)
--aparte-input-container-min-heightvar(--aparte-touch-target-size)
--aparte-elic-message-sizevar(--aparte-font-size-base)
--aparte-elic-title-sizevar(--aparte-font-size-base)
--aparte-elic-desc-sizevar(--aparte-font-size-sm)
--aparte-elic-option-title-sizevar(--aparte-font-size-md)
--aparte-elic-option-desc-sizevar(--aparte-font-size-sm)
--aparte-elic-skip-sizevar(--aparte-font-size-md)
--aparte-elic-step-sizevar(--aparte-font-size-sm)
--aparte-elic-option-paddingvar(--aparte-space-4) var(--aparte-space-5)
--aparte-conv-item-paddingvar(--aparte-space-4) var(--aparte-space-5)
--aparte-conv-item-font-sizevar(--aparte-font-size-md)

Component-specific radius (overridable per app)

Section titled “Component-specific radius (overridable per app)”
VariableDefaultNotes
--aparte-radius-inputvar(--aparte-radius-lg)
--aparte-radius-input-footervar(--aparte-radius-lg)palette only
--aparte-radius-send-btnvar(--aparte-radius-md)
--aparte-radius-action-btnvar(--aparte-radius-sm)
--aparte-radius-selectvar(--aparte-radius-md)
--aparte-radius-bubblevar(--aparte-radius-lg)

The bubble’s corner IS the bubble radius above

Section titled “The bubble’s corner IS the bubble radius above”

It was a 14px literal off the scale, the most visible corner of the transcript, and the knob named for it drove nothing.

VariableDefaultNotes
--aparte-message-content-radiusvar(--aparte-radius-bubble)
--aparte-radius-codevar(--aparte-radius-lg)
--aparte-radius-thinkingvar(--aparte-radius-lg)palette only
--aparte-radius-errorvar(--aparte-radius-md)palette only

They read a step of the scale directly, so a theme that wanted square menus and round bubbles had to move the step — and every other reader of it.

VariableDefaultNotes
--aparte-radius-menuvar(--aparte-radius-lg)
--aparte-radius-popovervar(--aparte-radius-lg)
--aparte-radius-dialogvar(--aparte-radius-xl)
--aparte-radius-tooltipvar(--aparte-radius-sm)
--aparte-avatar-error-ring0 0 0 2px var(--aparte-error-solid)The error ring on an avatar. A full shadow value, like —aparte-scroll-btn-shadow: the whole ring is the knob, not its width.
VariableDefaultNotes
--aparte-message-content-text-uservar(--aparte-text)
--aparte-message-content-text-assistantvar(--aparte-text)

The user’s bubble: a wash of the accent over the card surface

Section titled “The user’s bubble: a wash of the accent over the card surface”

It was a literal (#efe7f6 light / #2f2740 dark) — the one palette colour a consumer’s eight-line rebrand could not reach, so a chat moved to a blue brand kept a plum bubble. Mixed in sRGB rather than derived with oklch(from …), because a wash has to keep the SURFACE’s lightness, not the accent’s; 12 % is where the default palette reads as a tint, not a fill, on both grounds.

VariableDefaultNotes
--aparte-message-content-bg-usercolor-mix(in srgb, var(--aparte-primary) var(--aparte-mark-tint), var(--aparte-surface-1))The mark’s own tint: at 12 % the user bubble — the one mark that says “this turn is yours” — sat at 1.03:1 on the light page.
--aparte-surface-3color-mix(in srgb, var(--aparte-text) 6%, var(--aparte-surface-2))The third surface: the second, pulled 6 % toward the text. Close to what the old literals encoded (#e7dfce from #f1ebdf light, #342a40 from #2a2333 dark — 5 to 9 % per channel, not one figure), now one rule that follows the masters.

Text on the text colour used as a fill (the tooltip, the avatar)

Section titled “Text on the text colour used as a fill (the tooltip, the avatar)”

The card surface is the lightest tone in light and the darkest-but-one in dark, which is what the two literals were.

VariableDefaultNotes
--aparte-text-inversevar(--aparte-surface-1)
--aparte-surfacevar(--aparte-surface-1)
--aparte-surface-hovervar(--aparte-surface-2)
--aparte-text-secondaryvar(--aparte-text-muted)
--aparte-accentvar(--aparte-primary)palette only
--aparte-success-bordervar(--aparte-success)palette only
--aparte-success-surfacecolor-mix(in oklch, var(--aparte-success) 12%, transparent)
--aparte-error-border-softvar(--aparte-error)palette only
--aparte-error-surfacecolor-mix(in oklch, var(--aparte-error) 12%, transparent)

The small controls of a turn and of a conversation row are the recipe’s small step (24px, the WCAG 2.5.8 floor). The action bar used to feed 28 with a 24 exception for the last user turn, the branch arrows redrew 20 over their own --sm, the row’s redrew 20: three control heights in one 14px row.

VariableDefaultNotes
--aparte-action-bar-btn-sizevar(--aparte-btn-size-sm)
--aparte-conv-action-btn-sizevar(--aparte-btn-size-sm)
--aparte-action-bar-btn-colorvar(--aparte-text-muted)
--aparte-action-bar-btn-hover-bgvar(--aparte-surface-2)
--aparte-action-bar-btn-hover-colorvar(--aparte-text)
VariableDefaultNotes
--aparte-branch-picker-btn-colorvar(--aparte-text-muted)
--aparte-branch-picker-btn-hover-colorvar(--aparte-text)
--aparte-branch-picker-label-colorvar(--aparte-text-muted)
VariableDefaultNotes
--aparte-avatar-bg-uservar(--aparte-primary)
--aparte-avatar-text-uservar(--aparte-text-inverse)
--aparte-avatar-bg-assistantvar(--aparte-surface-3)
--aparte-avatar-text-assistantvar(--aparte-text)
VariableDefaultNotes
--aparte-input-bgvar(--aparte-surface-1)
--aparte-input-bordervar(--aparte-border)
--aparte-input-textvar(--aparte-text)
--aparte-input-placeholdervar(--aparte-text-muted)
--aparte-input-focus-bordervar(--aparte-primary)

It used to inherit the prose’s size and leading (16px on 1.7) under the browser’s generic monospace — the largest, airiest text in the message.

VariableDefaultNotes
--aparte-code-block-font-sizevar(--aparte-font-size-md)
--aparte-code-block-line-heightvar(--aparte-line-height-normal)
VariableDefaultNotes
--aparte-content-line-heightvar(--aparte-line-height-loose)
--aparte-cursor-colorvar(--aparte-primary)palette only
VariableDefaultNotes
--aparte-conv-item-radiusvar(--aparte-radius-md)
--aparte-conv-item-colorvar(--aparte-text-muted)
--aparte-conv-item-color-activevar(--aparte-text)
--aparte-conv-item-bg-hovervar(--aparte-surface-3)
--aparte-conv-item-bg-activevar(--aparte-surface-3)
--aparte-conv-item-font-weight-activevar(--aparte-font-weight-medium)

Read by a component with a built-in default rather than declared in :root. Custom properties inherit, so you set them exactly like the others — on :root, on a theme class, or on one <aparte-chat> to scope them to that chat. The default is what applies when you do not.

VariableDefaultRead by
--aparte-alert-intentstyles/display/alert.css
--aparte-approval-args-max-height8.5remstyles/components/elicitation.css
--aparte-approval-args-sizevar(--aparte-font-size-sm)styles/components/elicitation.css
--aparte-approval-gap4pxstyles/components/elicitation.css
--aparte-approval-option-padding8px 10pxstyles/components/elicitation.css
--aparte-approval-option-radius8pxstyles/components/elicitation.css
--aparte-approval-option-size0.85remstyles/components/elicitation.css
--aparte-badge-intentstyles/display/badge.css
--aparte-badge-intent-inkstyles/display/badge.css
--aparte-badge-on-intentstyles/display/badge.css
--aparte-btn-intentstyles/button.css
--aparte-btn-intent-inkstyles/button.css
--aparte-btn-on-intentstyles/button.css
--aparte-checkbox-intentstyles/field.css
--aparte-checkbox-on-intentstyles/field.css
--aparte-composer-row-control-sizevar(--aparte-input-action-btn-size)styles/components/bubble.css, styles/components/composer.css
--aparte-derived-inkstyles/button.css, styles/display/badge.css, styles/field.css
--aparte-dialog-widthstyles/surface/dialog.css
--aparte-elic-input-size0.85remstyles/components/elicitation.css
--aparte-elic-ring-roomstyles/components/elicitation.css
--aparte-mark-intentstyles/display/mark.css
--aparte-mark-intent-inkstyles/display/mark.css
--aparte-menu-max-widthstyles/surface/menu.css
--aparte-menu-min-widthstyles/surface/menu.css
--aparte-on-errorvar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-on-infovar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-on-neutralvar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-on-primaryvar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-on-secondaryvar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-on-successvar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-on-warningvar(--aparte-derived-ink)styles/button.css, styles/display/badge.css, styles/field.css
--aparte-popover-max-widthstyles/surface/popover.css
--aparte-radio-intentstyles/field.css
--aparte-range-intentstyles/field.css
--aparte-scroll-rail-bar0pxstyles/components/scroll-rail.css
--aparte-scroll-rail-block-end0pxstyles/components/scroll-rail.css
--aparte-scroll-rail-block-start0pxstyles/components/scroll-rail.css
--aparte-select-bg&lt;default>interop/element-props.ts, styles/primitives/select.css
--aparte-select-bordervar(--aparte-border-control)styles/primitives/select.css
--aparte-select-border-hovervar(--aparte-primary)styles/primitives/select.css
--aparte-select-chevronvar(--aparte-text-muted)styles/primitives/select.css
--aparte-select-dropdown-bgvar(--aparte-surface-1)styles/primitives/select.css
--aparte-select-option-hovervar(--aparte-surface-2)styles/primitives/select.css
--aparte-select-option-selectedcolor-mix(in srgb, var(--aparte-primary) var(--aparte-mark-tint), transparent)styles/primitives/select.css
--aparte-select-option-selected-textvar(--aparte-select-text, var(--aparte-text))styles/primitives/select.css
--aparte-select-textvar(--aparte-text)primitives/select/aparte-option.ts, styles/primitives/select.css
--aparte-select-zvar(--aparte-z-dropdown)styles/primitives/select.css
--aparte-send-disabled-bgvar(--aparte-surface-2)styles/components/composer.css
--aparte-spinner-colorcurrentColorstyles/primitives/progress-spinner.css
--aparte-spinner-trackvar(--aparte-border-control)styles/primitives/progress-spinner.css
--aparte-switch-intentstyles/field.css
--aparte-switch-thumb-sizestyles/field.css
--aparte-switch-trackstyles/field.css
--aparte-tooltip-max-widthstyles/surface/tooltip.css