Skip to content

Releases: lekoala/actual-css

0.16.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 16:59

Fixed

  • dialog.js: dialogs inside custom elements or shadow roots keep their native lifecycle and commands unless delegated with data-enhance="dialog" or the dialog API.
  • .range: the [dir="rtl"] fill fallback is its own rule, so browsers without :dir() keep it.
  • .range: an LTR island inside an RTL region fills from the left.

Added

  • enhance() accepts a third { accept } argument to filter lifecycle membership across subtree moves.
  • components.json: a docs URL per component, read from the CSS Docs: header (tab → enhancements/tabs.html, join → forms/joined-controls.html).
  • actual-css/tokens.json: the public global tokens, generated with components.json and verified by check:catalog.
  • menu.css: Docs: header pointing at the flyout page.
  • .field-group.flush: a fieldset without its frame, the legend as a plain label above the options.
  • .rating[role="img"]: read-only rating display on one element, with --rating-value (fractions draw partial stars) and --rating-max.
  • Avatar docs: an initials trigger keeps its initials in the accessible name (WCAG 2.5.3).
  • Card docs: group a title and its subtitle in an <hgroup> inside the header slot, which splits direct children.
  • Progressive enhancement guide: which runtime entry to load with a bundler, per enhancer, or without a build step.
  • Steps docs: a word wider than its share breaks inside the word; --step-min keeps whole words.

Changed

  • .avatar-stack: default --avatar-stack-overlap is an eighth of the size instead of a quarter, so two-letter initials stay readable.
  • Choice cards docs: the first demo groups cards in .field-group.flush.
  • package.json#homepage points at the docs site (/actual-css/site/) instead of the redirect page.
  • tokens.css and theme.css headers list their internal tokens in an Internal: section; --surface-link is internal.
  • generate:catalog fails when a file outside core lists a public global token under Internal: without a (local value …) note.
  • Bun 1.4.4 (packageManager); CI reads the version from there.
  • data-grid.html: data-grid-component 3.8.0, with row details in the component's own modal dialog.
  • data-grid.html: sets the --modal-size and --modal-pad hooks the data-grid theme reads for that dialog.
  • data-grid.html: grid buttons use .btn.surface instead of .btn.outline, so pagination and row actions keep the theme's light border.
  • date-fields.html, contrast-contexts.html, neo-brutalist.html: @lekoala/date-picker 0.6.0.
  • combobox.html, contrast-contexts.html, neo-brutalist.html: @lekoala/combobox 0.8.0.

0.15.1

Choose a tag to compare

@github-actions github-actions released this 26 Oct 08:20

Changed

  • Reset: -webkit-tap-highlight-color: transparent on html, so the rectangular native tap flash no longer covers rounded controls.
  • Bare links thicken their underline on :active as well as :hover, as touch feedback.
  • Bare text links (a[href] with no class) keep a tap flash tinted from their own color.

0.15.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 20:21

Added

  • .tabs-attached and .tab-panel: folder-style horizontal tabs joined to their panel, with shared surface, padding and shadow hooks.
  • --control-default-bg: inherited resting field fill shared with external widget bridges.
  • theming.md: a custom button focus recipe that preserves the outline and elevation when focus and hover coincide.
  • modular-import.md: replacing a component while keeping its markup, states and theme tokens, with a minimal Steps recipe linked from Philosophy.
  • neo-brutalist demo theme: pastel intents under black ink, offset shadows on framed objects, lifting buttons, a plus accordion marker.
  • demo/templates/neo-brutalist.html: a risograph print studio page on that theme, with page-loaded fonts.
  • Theming docs: where a font goes when a theme is in use (:root, not [data-theme]).
  • Card docs: tones (.card.soft plus an intent class).
  • Date picker docs and date-fields.html: the token bridge maps the panel shadow to --shadow-popout.
  • --btn-shadow: a resting button elevation that inherits and that the solid hover overlay keeps.
  • --control-shadow: a resting elevation for .input, .textarea and .select that keyboard focus keeps.
  • --table-header-bg and --table-header-fg: theme hooks for the .table header band, also read by the data grid template bridge.
  • --accordion-marker-icon and --accordion-marker-open-rotate: swap the accordion marker glyph and its open turn (plus to cross with --icon-plus and 45deg).
  • .badge > .avatar: a leading avatar sized to the badge for person chips (mention, assignee, contributor).
  • .timeline: vertical history of dated events, with intent dots and an optional avatar or icon slot.
  • Dashboard and Admini demos: activity feeds use .timeline instead of local feed CSS.
  • Diagrams docs and diagrams.html: Pele 1.6.0 on Actual tokens through a demo <pele-diagram> web component.
  • File upload docs and file-upload.html: Dropzone 6.3.5 rows composed from .list, .progress and .close, and FilePond 5.0.0-beta.74 on a --default-* token bridge.

Fixed

  • ::selection: SVG text uses the theme's selection foreground, keeping Pele diagram labels legible when selected.
  • .card.solid: bare and .prose links follow the card's text color at rest, hover and keyboard focus, including when a theme sets --link.
  • .skeleton: --skeleton-track and --skeleton-highlight are translucent ink tints, so a placeholder keeps its edges on cards, soft cards and dark surfaces.
  • .choice-card.chip: --state-selected-text takes priority over explicit intents to keep checks legible on light selected fills.
  • .alert with .alert-icon: an icon taller than one line, or an alert stretched by an equal-height grid, no longer opens a gap between the title and the body.

Removed

  • The bootstrap-v6 demo theme: the core focus line and field treatment it demonstrated are now the defaults.

Changed

  • .badge > .close: a trailing disc inside the pill, mirroring a leading .avatar; a transparent hit area keeps the 24px target.
  • edge theme: the default button drops its hover overlay through --btn-hover-overlay instead of box-shadow: none.
  • Date picker docs and demos: @lekoala/date-picker 0.5.1; focus maps through --dp-focus / --dp-focus-width instead of two override rules.
  • Date picker docs and demos: setDefaultControlClasses() gives every calendar header real .select, .input and .btn controls.
  • Combobox docs and demos: @lekoala/combobox 0.7.1; the control reads --border-width, --control-shadow and --control-hover-border like .input.
  • Combobox docs and demos: setDefaultChipClass() gives every chip the .badge primary skin without per-element attributes.
  • data-grid.html: data-grid-component 3.7.0 with themes/actual.css, global Actual control classes and responsive column disclosure.
  • contrast-contexts.html and neo-brutalist.html: combobox and date picker bridges follow the current recipes (0.7.1 / 0.5.1).
  • @lekoala/floating 0.3.1: repositionAt() throws on an unknown placement outside a browser too.

0.14.1

Choose a tag to compare

@github-actions github-actions released this 14 Oct 08:54

Added

  • actual-css/js/enhance logs a console warning when a second copy of the runtime loads (dist bundle plus source modules, or two bundles).

Changed

  • @lekoala/floating 0.3: a wrapped inline trigger flipped above anchors to its first line, and start/end alignment follows CSS direction without a dir attribute.
  • data-tooltip-placement and data-flyout-placement: an unknown value logs one console warning and falls back to the default placement.
  • dist/, site/ and demo/assets/ are no longer committed: run bun run build:all after a clone; npm, GitHub releases and Pages ship the CI build.
  • GitHub Pages deploys from CI (build:pages, scripts/pages.js) instead of serving the branch.

Fixed

  • .color-swatch: an inline swatch (a flyout trigger beside its label) centers on the text instead of sitting above the baseline.
  • data-enhance-config: a relaxed config keeps key:-like text inside a double-quoted string ({url: "http://x"}) instead of throwing.

0.14.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 16:59

Breaking changes

  • --soft-border-mix has no default: read it as var(--soft-border-mix, 100%) for the soft tint or var(--soft-border-mix, 65%) for the opaque .soft mix.

Added

  • .color-swatch: palette color on a native radio or checkbox, or a <button> opening a palette flyout, with --color-swatch-value, --color-swatch-size and --color-swatch-radius.
  • .segmented / .segment: segmented control for radios, a role="tablist" or navigation links, with --segmented-bg, --segmented-pad, --segmented-radius, --segmented-fg, --segmented-selected-bg and --segmented-selected-border.
  • demo/templates/order-management.html: one state at three levels — a badge in a selectable list, a contextual alert, and a notification with its own action — plus a critical alert, with no new framework API.
  • prefers-reduced-transparency: reduce paints .topbar and .form-actions.sticky opaque without backdrop blur.
  • The glass demo theme switches to opaque surfaces and drops its blur under prefers-reduced-transparency: reduce.
  • shot:states accepts a transparency state.
  • --switch-knob-inline-size sets the switch knob width for a capsule knob; the travel follows it.
  • demo/templates/settings-ios.html: iOS-like controls and grouped settings from unchanged markup, with the local CSS each gap needs.
  • admin-neutral demo theme: light-only business interface with a grey page, white cards, neutral actions and flat semantic tints.
  • --state-selected-text: selected ink on a regular surface (current .nav-link, selected .tab, current step number and ring), falling back to --state-selected.
  • actual-css contrast measures selected ink on each surface and the solid -fg pairs; a gradient fill reads n/a.
  • components.json: pseudoElements lists the ::before/::after each component generates, keyed by the element that owns them (.nav-link, .accordion summary).
  • actual-css design writes a portable dtcg/ token profile beside figma/ and penpot/, with spec $types (fontWeight, fontFamily, duration, cubicBezier, number) and the font stacks, line heights and motion figma/ drops.

Fixed

  • .color shows a round swatch the height of a .switch on a control-sized hit area, with the focus ring on the swatch; --color-size sets it.
  • A flyout no longer closes when the arrow keys or Space move through a radio group inside it.
  • Opening a flyout from the keyboard focuses a radio group at its checked radio, not its first one.
  • .list-item moves its trailing region to a new line when it no longer fits beside the longest word, instead of breaking .list-item-title mid-word.
  • .truncate inside .list-item-content keeps the trailing region on its line.
  • actual-css design writes DTCG colors with a 6-digit hex and alpha separate; Penpot still receives the alpha in its hex.
  • .scroll-snap keeps a focused item's ring inside its own clip on all four sides.
  • .alert.compact and .alert.spacious step --alert-pad-block.
  • .close stays visible in forced colors on a light-on-dark ink such as a .solid admonition title.
  • .app-nav frames the current tile in forced colors with a transparent border on a ::before overlay, so selecting a tile never shifts its icon or label and no Highlight repaint is needed.
  • --state-selected-text resets on every theme boundary, so a nested theme island reads its own --state-selected instead of inheriting the parent's selected ink.
  • Demo themes clear 4.5:1 on their solid pairs: bootstrap-v6 primary, success and danger; spruce and petrol warning; petrol secondary; edge warning; brutalist secondary and danger.
  • Demo themes set --state-selected-text where the primary cannot be both fill and page text: bootstrap-v6, brutalist, cyberpunk, gradient, ocean, square, sunset.
  • bootstrap-v6 filled badges keep their fill on hover.
  • The edge demo theme's default button reads its intent through --soft-fg-mix.
  • The glass demo theme keeps the default --soft-bg-mix and sets --soft-border-mix: 70%, so soft tints show their intent.
  • .alert.admonition title bar follows .solid, .surface and .outline; .solid previously left white ink on the soft tint.

Changed

  • admin-neutral uses emerald success fills with white solid ink, amber warning fills with dark ink, lighter neutral badges and white admonition bodies with neutral frames.
  • demo/templates/order-management.html softens the address notice, makes New order secondary and tightens the Orders header.
  • demo/templates/order-management.html pins admin-neutral and adds an Orders-list composition beside the existing three-level narrative.
  • .alert.admonition .alert-title is semibold.
  • .alert.admonition.solid frames its body with the neutral --border instead of the intent.
  • .badge, .alert and .btn.soft paint a translucent intent tint with no rim; the shared .soft on .card, .navbar, .app-nav and .chat-bubble stays an opaque mix.
  • --soft-hover-alpha defaults to 6%.
  • actual-css contrast measures soft pairs composited on --surface, --surface-raised and --surface-subtle.
  • Demo themes retune --soft-fg-mix (bootstrap-v6, corporate, cyberpunk, dim, edge, gradient, indigo, lavender, material, square) and --soft-hover-alpha (lavender, neon) for the soft tint.

0.13.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 18:48

Breaking changes

  • --cluster-justify, --cluster-align and --cluster-wrap no longer inherit into nested .cluster rows; set them on the row itself or select nested rows.
  • --grid-columns and --grid-min no longer inherit into a nested .grid.
  • .avatar-stack no longer infers its size from child .avatar.sm/.lg; set .sm/.lg on the stack and omit size classes on its avatars.
  • --focus-outline has no default value: it is a fallback-only hook that replaces the generic focus line; read var(--focus-ring-width) solid var(--focus-outline-color) instead.
  • data-enhance="tabs" needs role="tablist" on the same element; without it the runtime warns and wires nothing.
  • .nav-link shows its current state only for aria-current="page" or "location", and a.list-item only for "page" or "true"; any other value, "false" included, is inert.

Added

  • actual-css/js/full now exposes enhance, applyEnhancement, and registerEnhancement from the same runtime that registers the built-ins.
  • --tab-flex on .tabs sets each tab's flex; 1 1 0 gives equal-width tabs.
  • In supporting browsers, an overflowing link .tabs strip or .steps-horizontal row starts scrolled to its aria-current item, with no runtime (scroll-initial-target).
  • --border-control sets the resting edge of fields, choices, switches, OTP cells, choice cards, .join-addon and the color and file inputs; it defaults to --border, which keeps separating surfaces.

Changed

  • .steps-horizontal fits short labels on a phone, wraps (and hyphenates) long names, and aligns its outer stacked steps with the row's edges at every width.
  • .steps-horizontal --step-min defaults to the marker width and only sets a wider budget; --step-gap also separates adjacent steps.
  • forms/form-actions no longer has to load after layout/cluster: .form-actions relays its hooks into the row's --cluster-*.
  • An overflowing horizontal .tabs strip shows its selected tab in full, at connect and whenever selection changes, scrolling the strip only.
  • .floating-field accepts its .field-label before or after the control; state is read through :has() on the cell.
  • .list-item regions center on the row by default; add .items-start to the row or the .list for rows with long supporting text.
  • A horizontal .tabs strip never wraps, nor do its labels: a strip that does not fit scrolls horizontally.
  • .breadcrumb never wraps: a trail that does not fit truncates its labels with an ellipsis, ancestors before the current page.
  • A navigation flyout .tab shows the current state with aria-current="true" while its panel holds the current page.
  • .breadcrumb ancestor labels truncate on their <a> or <span>; the current page also truncates as bare text in an <li aria-current="page">.
  • The current .nav-list link trait sits inside the row's inline padding with rounded ends, instead of flush on its rounded edge.
  • A horizontal .tabs strip hides its scrollbar; selecting or focusing the clipped tab scrolls it into view.
  • An overflowing horizontal .tabs strip fades the edges it can still scroll toward, where scroll-driven animations are supported.
  • .scroller no longer styles the scrollbar on touch screens, where the native overlay scrollbar, hidden at rest, stays.

Fixed

  • actual-css contrast --theme FILE and actual-css design --theme FILE accept a theme written on :root and measure its own values, not the default palette.
  • A default .btn.outline in a field .join takes --border-control like the field, not --border.
  • The scrolling body of a dialog.modal.scrollable and a dialog.drawer keeps the focus line of its first and last controls inside its clip.
  • actual-css contrast and actual-css design detect a theme written [data-theme = "brand"] or [data-theme=brand], and ignore selectors quoted in comments.
  • actual-css contrast measures a theme whose name contains & or < under its own name, instead of an island the name had broken.
  • actual-css contrast and actual-css design reject a --name the theme file does not declare as [data-theme], instead of measuring the framework defaults.
  • A bare heading in a dialog.modal > header drops its block margins, so the band no longer grows and the title centers on the close.
  • A <nav> around .breadcrumb or .tabs shrinks inside a flex or grid row, so the trail truncates or the strip scrolls instead of the page.
  • An enhanced tablist no longer operates the tabs of a nested role="tablist" that has no data-enhance="tabs".
  • A tablist with data-enhance="tabs" sets its roving tabindex when a classic script in <head> connects it before its tabs are parsed.
  • The generic focus line of links, summary and nav items follows a --focus-ring-width set on a [data-theme] island or any region, like the .btn ring beside it.
  • The focus reserve of a scrolling dialog or drawer body is --focus-outline-offset + --focus-ring-width, no longer widened by 2 × --border-width.

0.12.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 13:11

Breaking changes

  • .actions is removed; compose a <menu> (or <ul>) with cluster for a row, or stack list-reset for a vertical list.
  • .navbar-nav is removed; the horizontal link list is a .cluster with the framework --gap.
  • .dialog-confirmation is removed; compose .media with .dialog-icon, then <footer class="bleed background-subtle cluster justify-content-space-between"> (recipe in docs/pages/components/dialog.md).
  • The dialog.modal body <footer> has no layout of its own; compose its row as <footer class="cluster justify-content-end">, as in the drawer.
  • --modal-header-bg is removed; put a .background-* utility on the dialog.modal > header band.
  • .form-actions no longer lays out its row; write class="cluster form-actions" (--form-actions-justify and --form-actions-align still apply).
  • .form-actions drops its top margin and --form-actions-margin-block-start; space it from the fields with .stack on the form.
  • .justify-content-* no longer sets --cluster-justify, so it aligns only its own element, never a nested .cluster.
  • .card header and footer rows drop to zero specificity: .media, .cluster, .form-actions and .justify-content-* on a slot now take over its layout.
  • --font-width-dense is removed; set font-stretch on an element that should render condensed.
  • A theme sets --control-pad-x-sm / -md / -lg instead of --control-pad-x, which .sm/.lg and .compact/.spacious now select from.
  • A theme that sets its own control steps re-selects them in its block to work as a nested island (recipe in docs/pages/foundations/tokens.md).

Added

  • actual-css contrast [--theme FILE] (Bun) measures a theme's soft, focus, invalid-focus and inverse pairs in each declared scheme, and exits 1 on a miss.
  • .tight and .loose set the inherited --gap to --space-20 / --space-50 alone, without the padding or control size of .compact / .spacious.
  • docs/pages/guides/upgrading.md lists the edits a 0.11 project needs for 0.12.
  • .items-baseline aligns children on their first text baseline and sets --items-align, completing the .items-* series.

Changed

  • An action row composed from .cluster inherits the framework --gap (0.75rem); the removed .actions set 0.5rem. Use .tight for the tighter rhythm.
  • .cluster removes the native list chrome (margin, padding, list-style) from a ul, ol or menu it is applied to; the element stays a list in the DOM.
  • A heading that is the first child beside a leading .dialog-icon in a .media row centers on the icon anywhere, not only in a dialog.
  • A dialog.modal or .drawer header gives back its corner-close room when the dialog contains no .close; without :has() the room stays.
  • .compact and .spacious no longer set font-stretch: density changes rhythm and geometry, never typography.
  • .compact and .spacious select --control-pad-x-sm / -lg, so density controls get narrower or wider, not only shorter or taller.
  • .sm controls use the shared --control-pad-x-sm step: 13px of inline padding instead of 14px.
  • .card.compact lowers --card-gap to --space-20 along with --card-pad.
  • .nav-list rows sit --space-10 apart instead of --space-20, so a vertical nav reads as one list.
  • .topbar lays out its default row at zero specificity, so topbar cluster hands the row to .cluster.
  • The WCAG and OKLCH color math moves from scripts/utils/color.js to src/tooling/color.js.
  • report:theme-contrast labels a dark-only preset (dim, glass, indigo, neon) as dark instead of light.

Fixed

  • actual-css contrast counts a resting soft pair under 4.5:1 as a miss; it only gated the hovered pair.
  • The design and contrast examples run bunx --bun actual-css: plain bunx honors the CLI's Node shebang and stops on "needs Bun".
  • dialog.modal.scrollable lays the body wrapper out as a flex column: a structural header or footer keeps its size and every other region scrolls, so a modal with an external dialog > header no longer collapses its footer to zero.
  • --dialog-icon-size and --dialog-icon-glyph-size are read, not declared, on .dialog-icon, so an override on the icon's media row or the dialog moves the circle and the heading beside it together.
  • An .avatar-stack avatar layers its fill over --surface-opaque, so a translucent fill no longer shows the neighbour it overlaps.
  • prefers-reduced-motion also resets ::backdrop, so a modal or drawer scrim no longer fades when the user reduces motion.

0.11.1

Choose a tag to compare

@github-actions github-actions released this 30 Sep 21:18

Added

  • dialog.modal > header provides aligned icon/title/action chrome through --modal-header-bg; modal body wrappers expose --modal-pad and support .bleed bands.
  • actual-css design (Bun) exports a theme to Figma and Penpot tokens; see docs/pages/guides/design-tools.md.
  • actual-css design writes a Penpot plugin that builds Button, Input, Badge, Alert and Card bound to those tokens.
  • Soft fills and borders export as recipe tokens named <variant>-<intent>-<role> (soft-danger-bg).
  • scripts/serve.js --cors serves the repository to another origin, such as penpot.app loading a local plugin.
  • --meter-track sets the .meter track, matching --progress-track.
  • --close-bg sets the dialog corner .close background, so a dialog over a painted band can drop the default chip.
  • --close-color and --close-hover-color set the X ink at rest and on hover, so a bare X can take a painted band's foreground.
  • --close-hover-overlay routes the .close hover/active wash (matching --btn-hover-overlay); the modal/drawer corner sets it transparent so only the ink changes.

Changed

  • dialog.modal is borderless: the raised surface and shadow define the panel, so a full-bleed header, footer, or hero band no longer meets a hard edge; a forced-colors border keeps the outline.

Fixed

  • A corner .close that is a direct child of dialog.modal > header reserves inline room for itself; previously only the wrapper <header> did, so a long chrome title could run under it.
  • dialog.modal derives one --dialog-inner-radius for the body wrapper, the header band, and the .dialog-confirmation footer, so an edge band keeps the panel corner instead of taking its own radius.
  • The .dialog-confirmation action band spreads a two-action decision to its edges, keeping a cancel action apart from the committing one.
  • .dialog-confirmation centers its leading icon on the title while keeping supporting copy in the same text column.
  • .btn and the .file selector button take their borders out of the block padding, so they match .input height at every size instead of standing 2px taller outside a .join.
  • tests/browser/control-geometry.test.js measures controls outside a .join, whose stretch had hidden the mismatch.

0.11.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 06:01

Breaking changes

  • enhancementSelector() and hasEnhancement() are no longer exported from actual-css/js/enhance; query [data-enhance~="name"] directly.
  • registerCommands() drops the prepare callback; write aria-controls, aria-haspopup, and aria-pressed on command buttons in the markup.
  • registerCommands() and commandSelector() throw on a name that is neither a native command keyword nor a custom -- command.
  • FormValidator.init() is removed; the module self-registers, and applyEnhancement("validation", selector) opts forms in by selector.
  • The non-modal dialog command is command="--show"; command="show" is not an HTML keyword and no longer opens a dialog.
  • .choice-card shows its control on the leading edge — a ring (radio) or box (checkbox) at rest that fills with the check — instead of a corner badge shown only once checked; the badge is now the "Corner badge" recipe in docs/pages/forms/choice-cards.md.
  • .choice-card is a two-column grid: each child element of the label becomes a row beside the indicator, so loose text needs an element.
  • --choice-card-check-size defaults to 1.25em and also sets the width of the .choice-card indicator column.
  • --aura-width is the whole painted .aura frame (the border, no padding); --aura-width: 3px reproduces the former default frame.
  • .aura defaults to a thin lit line: --aura-width: 1px and --aura-intensity: 0.4.

Added

  • --surface-opaque and --surface-solid-fg let a theme paint translucent surfaces while rings, separators and inverse text stay opaque.
  • report:theme-contrast reports the --surface-solid-fg on --surface-solid text pair.
  • scripts/utils/color.js holds the WCAG contrast and OKLCH math for reports and browser tests; contrast() refuses translucent colors.
  • glass demo theme: translucent surfaces over a theme-painted backdrop, a stress test of the opaque surface roles.
  • tests/browser/translucent-surface.test.js keeps rings, separators, step markers, and inverse text opaque over translucent surfaces.
  • --choice-card-gap sets the space between a .choice-card indicator and its label.
  • tests/browser/choice-card.test.js locks the leading indicator: visible at rest, level with the first line of a wrapping label, and kept under forced colors.
  • .shimmer (actual-css/css/effects/shimmer) sweeps a highlight across a text label for an activity in progress; hooks --shimmer-color and --shimmer-highlight.
  • .range moves to a 1.125rem thumb on a 0.625rem track with a data-enhance="range" fill (--range-progress) and named datalist values (aria-valuetext plus output sync).
  • --flyout-pad gives rich flyout panels dialog air.
  • tests/browser/control-geometry.test.js locks the shared control block geometry: input, select, addon, and buttons (including busy and icon-only) share one height per size, and the spinner follows its context at 1em.

Changed

  • --indicator-ring, the .avatar-stack ring, the .range thumb separator, segmented .meter separators and .steps markers read --surface-opaque.
  • .tooltip text reads --surface-solid-fg.
  • .aura masks its frame under the child with --surface-opaque, so a transparent or translucent child is framed rather than filled.
  • The .aura far halo is an outer box-shadow, so it never paints under the child.
  • .status-bar without an intent paints --surface-solid / --surface-solid-fg instead of --text / --surface.
  • registerEnhancement() discovers every named behavior through one shared [data-enhance] query per root instead of one [data-enhance~="name"] selector each.
  • The command router calls event.preventDefault() once a target resolves, before handle; handlers no longer cancel the click themselves.
  • --focus-outline (the generic currentColor focus fallback) is --focus-ring-width wide instead of 2 × --border-width: 3px instead of 4px under prefers-contrast: more.

Fixed

  • actual-css/js/range synchronizes fill, aria-valuetext, and outputs after a native form reset, including controls associated through form="…", while respecting cancellation and teardown.
  • Docs search leaves Enter on the close button and IME confirmation alone, and ArrowUp from no selection reaches the last result.
  • demo/sites/admini/chat.html loads the shared composer recipe before local overrides so the bottom bar keeps its flat border and compact textarea.
  • .otp with .input on its native input no longer draws the field focus ring around all the cells.
  • Docs search keeps focus in the input while arrowing through results (aria-activedescendant + Home/End), so the second and later matches are reachable by keyboard; Enter follows the highlight instead of closing the dialog.
  • .tab and .rating draw the component focus ring (--focus-ring-width solid --focus): an intent on the rating no longer becomes the focus color, and neither ring is 4px under prefers-contrast: more.
  • commandSelector() matches native command keywords case-insensitively, as the router does.
  • The command router ignores a trigger disabled through a <fieldset disabled> ancestor.
  • tooltip.js shows on focus only when the trigger matches :focus-visible: a pointer press, or focus handed back by a dialog closed with the pointer, no longer brings the tooltip back.

Documentation and tooling

  • Choice cards: a stacked-list demo, and a "Corner badge" recipe (.choice-tile) that the Neon Ramen item page uses for its tiles.
  • tests/browser/choice-chip.test.js asserts the chip's dot layer keeps its height, not just its clip and colour.
  • tests/exports.test.js keys its inventory on every JS subpath of package.json#exports, so a new subpath or an export on an unlisted module now fails the suite.
  • Docs search ranks a query token inside a longer alias (select finds the Combobox page via searchable select) and matches body text on word boundaries, so selection no longer outranks select; Combobox and Forms carry select, custom select, and form controls aliases.
  • focus.css documents two focus contracts: text-like elements keep the currentColor fallback, boxed components draw the --focus ring, and an invalid field's --form-invalid-border ring is the one exception.
  • tests/css-audit.test.js fails on any framework outline outside the two focus contracts.
  • report:theme-contrast reports each preset's invalid-field focus line against the field background.
  • check:sync fails when the .sm and .lg participant lists in variants.css diverge.
  • Tailwind guide: .outline is named as the dangerous collision, with .table, .sticky, .truncate, .prose, and .sr-only.
  • Tailwind and Bootstrap guides: a prefix transform is consumer-pipeline work and must also rename the runtime state classes in actual-css/js/selectors.
  • Table: clarified the responsive boundary — horizontal overflow (.table-wrap + --table-min + accessible scroll) stays the strategy, and the docs now name the alternatives (list/media view, .data-list, dedicated data grid).
  • Badge vs choice chip vs removable tag vocabulary: a badge is information (non-interactive), choice-card.chip is a selectable label (never nest a .close inside it), and removable tags belong to the widget that produced them.
  • Combobox: a "Bridge contract" section names what Actual owns (tokens, focus, invalid, .join, contrast) versus the widget (behavior, multi-value, i18n), and keeps the bridge CSS in docs/demo.
  • Tabs: the .tabs strip / .tab trigger pair is documented as the container/item convention, with the tab module versus tabs token split stated.
  • Pagination: items are documented as .btn.ghost with chevrons on Previous/Next, so aria-current="page" is the only filled item.
  • Rich text: demo/templates/rich-text.html is now an @lekoala/rich-text integration (@0.2 from jsDelivr) instead of a hand-wired Squire prototype, with the skin reduced to a --rt-* token bridge plus a rich composer reusing .composer chrome.

0.10.0

Choose a tag to compare

@github-actions github-actions released this 25 Sep 18:49

Breaking changes

  • .inverted is removed: it mixed a contrasting surface with theme inversion while remapping only part of the palette. Use a data-theme island for a complete light/dark subtree, or paint a local band in application CSS.
  • .white-space-normal is renamed .text-wrap, the counterpart of .text-nowrap.
  • .dialog-close, .drawer-close, and .alert-dismiss are replaced by the shared .close (components/close.css); a modular build imports it next to its host.
  • A removable badge needs <button class="close">: a bare .badge > button is no longer styled, and an empty one no longer paints an X.
  • --alert-dismiss-size and --badge-dismiss-icon-size are replaced by --close-size / --close-icon-size.
  • The dialog corner applies to a .close that is a direct child of the dialog, of its form or header, or of a form inside that header.
  • getSurfaceAutoClose is removed from surface.js: it had no caller, and the code that opens a surface passes its own autoClose.
  • .column-span-1…12 and .column-start-1…12 are replaced by the --column-span / --column-start hooks on each .column-layout child.
  • The bare actual-css entrypoint resolves to the complete compiled framework, like actual-css/full, style, unpkg, and jsdelivr; import actual-css/css for the core alone.
  • package.json#main is removed.
  • dist/actual.css and dist/actual.min.css are no longer built or published; compose the core from actual-css/css.
  • dist/actual.js (the compiled loader alone) is no longer built or published; bundle your own entry from actual-css/js and actual-css/js/*.
  • .input, .textarea, .select, and an open custom select draw a --focus-ring-width inset outline in --focus instead of a focus border plus outer halo.
  • .join no longer moves a field's focus ring onto the group: each segment keeps its own indicator.
  • .btn, .close, .check, .radio, .switch, .choice-card, .file, and .color draw a solid --focus-ring-width line in --focus at --focus-outline-offset instead of a translucent halo.
  • .range thumb focus ring is solid --focus instead of --focus-ring.
  • --focus-ring and --focus-ring-shadow are removed; every focus indicator reads --focus.
  • --btn-focus-color and --btn-focus-ring-color are removed: a button's focus color no longer follows its intent.
  • .check, .radio, and .switch keep their resting border on focus instead of switching it to --focus.
  • The default --focus is hsl(268 30% 55%) in both schemes instead of var(--neutral): a primary-hued mid-tone that holds 3:1 against --surface and --surface-solid.
  • --focus-ring-width is 2px instead of 3px, and 3px instead of 4px under prefers-contrast: more.
  • bootstrap-v6 sets --focus: #3d8bfd and --focus-ring-width: 3px instead of its own ring recipe.

Added

  • .bleed is a shared surface contract: src/css/components/bleed.css reads the relayed --surface-pad, so a direct child of a .drawer or of an accordion panel escapes its inset like a card child already did.
  • --accordion-pad hook: the summary row and the panel no longer hard-code their inset, and the panel relays it so .accordion.separated items accept a .bleed child.
  • --accordion-marker-size hook.
  • .data-list.stacked: the value sits under its term instead of beside it.
  • --bleed-pad is a public read-only hook: padding: var(--bleed-pad) pads a .bleed band that is not a header, footer, or figure.
  • .join supports a control nested one wrapper level deep (e.g. a combobox element): the wrapper carries the group corners and primary-edge z-index, the inner control inherits the radius, and no widget is named.
  • .nav-link works on a <button>: UA button chrome is neutralized with <a>-identical metrics.
  • material demo theme: Material 3 baseline palette, filled underline fields and addons whose focus thickens the bottom line, pill buttons, and a joined outline action drawn as one more filled segment.
  • .choice-card.chip: compact pill form for filter chips and selectable tags, with a leading dot that turns into the check without moving the label.
  • --choice-chip-dot is a read-only hook on the chip's ::before: the check-slot centre, so a theme can grow the dot's circle() clip into a selected fill.
  • The material theme grows the chip dot into a tonal fill.
  • The gradient theme demos the outer-halo field focus; bootstrap-v6 and edge have no field focus override.
  • demo/templates/contrast-contexts.html: the page, data-theme islands, a card island, a named theme and an application-painted band with every intent variant, a form, a combobox, a date picker and focus lines measured live, plus a .join gallery with no focus adaptation.

Fixed

  • dialog.js only backdrop-dismisses when the press started on the backdrop: a text selection released over the backdrop no longer closes (or shakes) the dialog.
  • dialog.js restores the horizontal scroll position on open, not just the vertical one.
  • tab.js, focus-group.js, menu.js, and the flyout trigger ignore IME-composed and ctrl/alt/meta-modified keys; escape.js ignores composition too.
  • status.js restores its bar when the host dialog was removed from the DOM (htmx/Turbo swap): the next status() heals instead of no-opping forever.
  • tooltip.js resynchronizes shorthand data-tooltip text on every show, so a "Copy" → "Copied!" update displays without rewiring.
  • The text of a visible shorthand tip updates on the next show, never mid-flight.
  • flyout.js sets aria-haspopup="menu" only for a true role="menu" panel: <menu> carries the implicit list role.
  • dialog.js respects closedby="none" on cancel when HTMLDialogElement.closedBy is unavailable, while explicit close requests still work.
  • dialog.js restores generated ARIA attributes, title IDs, and rewritten closedby values on disconnect without overwriting author changes.
  • A last .bleed child of an accordion panel had square corners over the rounded .separated item or the group's last item.
  • applyEnhancement("validation", …) did not wire an already-connected form: validation.js now registers through registerEnhancement.
  • --app-nav-side-size was invisible to check:css-api (header read Public hook:).
  • A custom data-theme on <html> kept the default palette's --*-soft-fg calibration instead of resetting it, so the same theme resolved a different soft ink on <html data-theme> than on a nested island.
  • .nav-link hover/current and .app-nav hover fill with a tint of their own ink and follow --ui-fg, so they read on a bar with its own --ui-bg.
  • .flyout owns color: var(--text), like modal and drawer.
  • Preset themes: every --focus is a mid-tone that holds 3:1 on --surface and --surface-solid, and a --soft-fg-mix keeps every soft ink at 4.5:1 (report:theme-contrast lists no failing pair).
  • report:theme-contrast measures .badge.soft explicitly, so a preset that fills badges by default (bootstrap-v6) is no longer reported against a solid pair.
  • ocean theme: light --danger darkened (solid text 4.1:1 → 5.2:1), --soft-fg-mix: 50% (soft inks from 2.0–4.3:1 to ≥ 4.8:1), and a mid-tone --focus that holds 3:1 on both surfaces.
  • .join-addon honours --control-border-block-end-color / -width like text controls, so a theme's underline or accent edge reaches the addon segment.
  • gradient theme: the focus halo follows validation (--form-invalid-border), and it and material restyle the open customizable select with their field focus instead of the core :open outline.
  • --dialog-focus-bleed and --drawer-focus-bleed reserve max(--focus-ring-width, 2 × --border-width) past the offset, so a 3px component line (bootstrap-v6) is no longer clipped by 1px in a scrollable modal or drawer body.
  • A focused field in .join stacks above an adjacent field (z-index: 4 rule now follows the primary-edge rule), so the neighbour no longer covers its shared edge.
  • A field wrapped one level deep in .join fills its stretched wrapper (block-size: 100%) instead of stopping short of a taller sibling.
  • Focus indicators on a --surface-solid band and on a card nested in it hold 3:1 (a primary button's ring measured 1.1:1, a neutral one in a nested card 1.0:1).

Documentation and tooling

  • Combobox: the bridge recipe targets @lekoala/combobox 0.5.0, replaces its halo with the field inset outline, reads --form-invalid-border, and inherits .join corners.
  • tests/browser/field-focus.test.js asserts the field outline stays inside the border box (default and prefers-contrast) and that --focus holds 3:1 on --surface and --surface-solid in both schemes.
  • bun run report:theme-contrast also reports each preset's --focus against --surface and --surface-solid.
  • Theming: "Make a dark (or light) section" replaces the .inverted recipe — a data-theme island for a whole context, application CSS for a painted band.
  • Button: a dark button (formerly .btn.inverted) is a custom intent, --intent: var(--surface-solid) / --intent-fg: var(--surface).
  • demo/templates/surfaces.html is removed; contrast-contexts.html covers surface boundaries.
  • tests/browser/inverted.test.js becomes surface-context.test.js, on an application-painted band.
  • Pushing a version tag publishes the GitHub release: .npmrc keeps npm version on the bare tag format, bun run release:notes extracts (and validates) the dated changelog section, and the CI release job attaches dist/.
  • Theming: a "Replace the palette with my own brand" entry names the tinted neutrals and soft-ink tokens a partial override leaves behind, and points at the minimal recolor set.
  • Tokens: the minimal recolor theme includes --shadow-color.
  • Tokens: a "Where a token is declared" section separates global, component-scoped, and relay properties, and shows the `var(--card-p...
Read more