Repository navigation
Releases: lekoala/actual-css
Releases · lekoala/actual-css
Release list
0.16.0
Fixed
dialog.js: dialogs inside custom elements or shadow roots keep their native lifecycle and commands unless delegated withdata-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: adocsURL per component, read from the CSSDocs:header (tab →enhancements/tabs.html, join →forms/joined-controls.html).actual-css/tokens.json: the public global tokens, generated withcomponents.jsonand verified bycheck: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-minkeeps whole words.
Changed
.avatar-stack: default--avatar-stack-overlapis 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#homepagepoints at the docs site (/actual-css/site/) instead of the redirect page.tokens.cssandtheme.cssheaders list their internal tokens in anInternal:section;--surface-linkis internal.generate:catalogfails when a file outside core lists a public global token underInternal:without a(local value …)note.- Bun 1.4.4 (
packageManager); CI reads the version from there. data-grid.html:data-grid-component3.8.0, with row details in the component's own modal dialog.data-grid.html: sets the--modal-sizeand--modal-padhooks the data-grid theme reads for that dialog.data-grid.html: grid buttons use.btn.surfaceinstead 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-picker0.6.0.combobox.html,contrast-contexts.html,neo-brutalist.html:@lekoala/combobox0.8.0.
0.15.1
Changed
- Reset:
-webkit-tap-highlight-color: transparentonhtml, so the rectangular native tap flash no longer covers rounded controls. - Bare links thicken their underline on
:activeas 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
Added
.tabs-attachedand.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-brutalistdemo 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.softplus 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,.textareaand.selectthat keyboard focus keeps.--table-header-bgand--table-header-fg: theme hooks for the.tableheader band, also read by the data grid template bridge.--accordion-marker-iconand--accordion-marker-open-rotate: swap the accordion marker glyph and its open turn (plus to cross with--icon-plusand45deg)..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
.timelineinstead 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,.progressand.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.proselinks follow the card's text color at rest, hover and keyboard focus, including when a theme sets--link..skeleton:--skeleton-trackand--skeleton-highlightare translucent ink tints, so a placeholder keeps its edges on cards, soft cards and dark surfaces..choice-card.chip:--state-selected-texttakes priority over explicit intents to keep checks legible on light selected fills..alertwith.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-v6demo 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.edgetheme: the default button drops its hover overlay through--btn-hover-overlayinstead ofbox-shadow: none.- Date picker docs and demos:
@lekoala/date-picker0.5.1; focus maps through--dp-focus/--dp-focus-widthinstead of two override rules. - Date picker docs and demos:
setDefaultControlClasses()gives every calendar header real.select,.inputand.btncontrols. - Combobox docs and demos:
@lekoala/combobox0.7.1; the control reads--border-width,--control-shadowand--control-hover-borderlike.input. - Combobox docs and demos:
setDefaultChipClass()gives every chip the.badge primaryskin without per-element attributes. data-grid.html:data-grid-component3.7.0 withthemes/actual.css, global Actual control classes and responsive column disclosure.contrast-contexts.htmlandneo-brutalist.html: combobox and date picker bridges follow the current recipes (0.7.1 / 0.5.1).@lekoala/floating0.3.1:repositionAt()throws on an unknown placement outside a browser too.
0.14.1
Added
actual-css/js/enhancelogs a console warning when a second copy of the runtime loads (dist bundle plus source modules, or two bundles).
Changed
@lekoala/floating0.3: a wrapped inline trigger flipped above anchors to its first line, and start/end alignment follows CSSdirectionwithout adirattribute.data-tooltip-placementanddata-flyout-placement: an unknown value logs one console warning and falls back to the default placement.dist/,site/anddemo/assets/are no longer committed: runbun run build:allafter 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 keepskey:-like text inside a double-quoted string ({url: "http://x"}) instead of throwing.
0.14.0
Breaking changes
--soft-border-mixhas no default: read it asvar(--soft-border-mix, 100%)for the soft tint orvar(--soft-border-mix, 65%)for the opaque.softmix.
Added
.color-swatch: palette color on a native radio or checkbox, or a<button>opening a palette flyout, with--color-swatch-value,--color-swatch-sizeand--color-swatch-radius..segmented/.segment: segmented control for radios, arole="tablist"or navigation links, with--segmented-bg,--segmented-pad,--segmented-radius,--segmented-fg,--segmented-selected-bgand--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: reducepaints.topbarand.form-actions.stickyopaque without backdrop blur.- The glass demo theme switches to opaque surfaces and drops its blur under
prefers-reduced-transparency: reduce. shot:statesaccepts atransparencystate.--switch-knob-inline-sizesets 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-neutraldemo 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 contrastmeasures selected ink on each surface and the solid-fgpairs; a gradient fill reads n/a.components.json:pseudoElementslists the::before/::aftereach component generates, keyed by the element that owns them (.nav-link,.accordion summary).actual-css designwrites a portabledtcg/token profile besidefigma/andpenpot/, with spec$types(fontWeight,fontFamily,duration,cubicBezier,number) and the font stacks, line heights and motionfigma/drops.
Fixed
.colorshows a round swatch the height of a.switchon a control-sized hit area, with the focus ring on the swatch;--color-sizesets 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-itemmoves its trailing region to a new line when it no longer fits beside the longest word, instead of breaking.list-item-titlemid-word..truncateinside.list-item-contentkeeps the trailing region on its line.actual-css designwrites DTCG colors with a 6-digithexandalphaseparate; Penpot still receives the alpha in its hex..scroll-snapkeeps a focused item's ring inside its own clip on all four sides..alert.compactand.alert.spaciousstep--alert-pad-block..closestays visible in forced colors on a light-on-dark ink such as a.solidadmonition title..app-navframes the current tile in forced colors with a transparent border on a::beforeoverlay, so selecting a tile never shifts its icon or label and noHighlightrepaint is needed.--state-selected-textresets on every theme boundary, so a nested theme island reads its own--state-selectedinstead 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-textwhere 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-mixand sets--soft-border-mix: 70%, so soft tints show their intent. .alert.admonitiontitle bar follows.solid,.surfaceand.outline;.solidpreviously left white ink on the soft tint.
Changed
admin-neutraluses 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.htmlsoftens the address notice, makes New order secondary and tightens the Orders header.demo/templates/order-management.htmlpinsadmin-neutraland adds an Orders-list composition beside the existing three-level narrative..alert.admonition.alert-titleis semibold..alert.admonition.solidframes its body with the neutral--borderinstead of the intent..badge,.alertand.btn.softpaint a translucent intent tint with no rim; the shared.softon.card,.navbar,.app-navand.chat-bubblestays an opaque mix.--soft-hover-alphadefaults to6%.actual-css contrastmeasures soft pairs composited on--surface,--surface-raisedand--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
Breaking changes
--cluster-justify,--cluster-alignand--cluster-wrapno longer inherit into nested.clusterrows; set them on the row itself or select nested rows.--grid-columnsand--grid-minno longer inherit into a nested.grid..avatar-stackno longer infers its size from child.avatar.sm/.lg; set.sm/.lgon the stack and omit size classes on its avatars.--focus-outlinehas no default value: it is a fallback-only hook that replaces the generic focus line; readvar(--focus-ring-width) solid var(--focus-outline-color)instead.data-enhance="tabs"needsrole="tablist"on the same element; without it the runtime warns and wires nothing..nav-linkshows its current state only foraria-current="page"or"location", anda.list-itemonly for"page"or"true"; any other value,"false"included, is inert.
Added
actual-css/js/fullnow exposesenhance,applyEnhancement, andregisterEnhancementfrom the same runtime that registers the built-ins.--tab-flexon.tabssets each tab's flex;1 1 0gives equal-width tabs.- In supporting browsers, an overflowing link
.tabsstrip or.steps-horizontalrow starts scrolled to itsaria-currentitem, with no runtime (scroll-initial-target). --border-controlsets the resting edge of fields, choices, switches, OTP cells, choice cards,.join-addonand the color and file inputs; it defaults to--border, which keeps separating surfaces.
Changed
.steps-horizontalfits 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-mindefaults to the marker width and only sets a wider budget;--step-gapalso separates adjacent steps.forms/form-actionsno longer has to load afterlayout/cluster:.form-actionsrelays its hooks into the row's--cluster-*.- An overflowing horizontal
.tabsstrip shows its selected tab in full, at connect and whenever selection changes, scrolling the strip only. .floating-fieldaccepts its.field-labelbefore or after the control; state is read through:has()on the cell..list-itemregions center on the row by default; add.items-startto the row or the.listfor rows with long supporting text.- A horizontal
.tabsstrip never wraps, nor do its labels: a strip that does not fit scrolls horizontally. .breadcrumbnever wraps: a trail that does not fit truncates its labels with an ellipsis, ancestors before the current page.- A navigation flyout
.tabshows the current state witharia-current="true"while its panel holds the current page. .breadcrumbancestor labels truncate on their<a>or<span>; the current page also truncates as bare text in an<li aria-current="page">.- The current
.nav-listlink trait sits inside the row's inline padding with rounded ends, instead of flush on its rounded edge. - A horizontal
.tabsstrip hides its scrollbar; selecting or focusing the clipped tab scrolls it into view. - An overflowing horizontal
.tabsstrip fades the edges it can still scroll toward, where scroll-driven animations are supported. .scrollerno longer styles the scrollbar on touch screens, where the native overlay scrollbar, hidden at rest, stays.
Fixed
actual-css contrast --theme FILEandactual-css design --theme FILEaccept a theme written on:rootand measure its own values, not the default palette.- A default
.btn.outlinein a field.jointakes--border-controllike the field, not--border. - The scrolling body of a
dialog.modal.scrollableand adialog.drawerkeeps the focus line of its first and last controls inside its clip. actual-css contrastandactual-css designdetect a theme written[data-theme = "brand"]or[data-theme=brand], and ignore selectors quoted in comments.actual-css contrastmeasures a theme whose name contains&or<under its own name, instead of an island the name had broken.actual-css contrastandactual-css designreject a--namethe theme file does not declare as[data-theme], instead of measuring the framework defaults.- A bare heading in a
dialog.modal > headerdrops its block margins, so the band no longer grows and the title centers on the close. - A
<nav>around.breadcrumbor.tabsshrinks 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 nodata-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,
summaryand nav items follows a--focus-ring-widthset on a[data-theme]island or any region, like the.btnring beside it. - The focus reserve of a scrolling dialog or drawer body is
--focus-outline-offset+--focus-ring-width, no longer widened by2 × --border-width.
0.12.0
Breaking changes
.actionsis removed; compose a<menu>(or<ul>) withclusterfor a row, orstack list-resetfor a vertical list..navbar-navis removed; the horizontal link list is a.clusterwith the framework--gap..dialog-confirmationis removed; compose.mediawith.dialog-icon, then<footer class="bleed background-subtle cluster justify-content-space-between">(recipe indocs/pages/components/dialog.md).- The
dialog.modalbody<footer>has no layout of its own; compose its row as<footer class="cluster justify-content-end">, as in the drawer. --modal-header-bgis removed; put a.background-*utility on thedialog.modal > headerband..form-actionsno longer lays out its row; writeclass="cluster form-actions"(--form-actions-justifyand--form-actions-alignstill apply)..form-actionsdrops its top margin and--form-actions-margin-block-start; space it from the fields with.stackon the form..justify-content-*no longer sets--cluster-justify, so it aligns only its own element, never a nested.cluster..cardheader and footer rows drop to zero specificity:.media,.cluster,.form-actionsand.justify-content-*on a slot now take over its layout.--font-width-denseis removed; setfont-stretchon an element that should render condensed.- A theme sets
--control-pad-x-sm/-md/-lginstead of--control-pad-x, which.sm/.lgand.compact/.spaciousnow 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..tightand.looseset the inherited--gapto--space-20/--space-50alone, without the padding or control size of.compact/.spacious.docs/pages/guides/upgrading.mdlists the edits a 0.11 project needs for 0.12..items-baselinealigns children on their first text baseline and sets--items-align, completing the.items-*series.
Changed
- An action row composed from
.clusterinherits the framework--gap(0.75rem); the removed.actionsset 0.5rem. Use.tightfor the tighter rhythm. .clusterremoves the native list chrome (margin,padding,list-style) from aul,olormenuit is applied to; the element stays a list in the DOM.- A heading that is the first child beside a leading
.dialog-iconin a.mediarow centers on the icon anywhere, not only in a dialog. - A
dialog.modalor.drawerheader gives back its corner-close room when the dialog contains no.close; without:has()the room stays. .compactand.spaciousno longer setfont-stretch: density changes rhythm and geometry, never typography..compactand.spaciousselect--control-pad-x-sm/-lg, so density controls get narrower or wider, not only shorter or taller..smcontrols use the shared--control-pad-x-smstep: 13px of inline padding instead of 14px..card.compactlowers--card-gapto--space-20along with--card-pad..nav-listrows sit--space-10apart instead of--space-20, so a vertical nav reads as one list..topbarlays out its default row at zero specificity, sotopbar clusterhands the row to.cluster.- The WCAG and OKLCH color math moves from
scripts/utils/color.jstosrc/tooling/color.js. report:theme-contrastlabels a dark-only preset (dim,glass,indigo,neon) as dark instead of light.
Fixed
actual-css contrastcounts a resting soft pair under 4.5:1 as a miss; it only gated the hovered pair.- The
designandcontrastexamples runbunx --bun actual-css: plainbunxhonors the CLI's Node shebang and stops on "needs Bun". dialog.modal.scrollablelays 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 externaldialog > headerno longer collapses its footer to zero.--dialog-icon-sizeand--dialog-icon-glyph-sizeare 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-stackavatar layers its fill over--surface-opaque, so a translucent fill no longer shows the neighbour it overlaps. prefers-reduced-motionalso resets::backdrop, so a modal or drawer scrim no longer fades when the user reduces motion.
0.11.1
Added
dialog.modal > headerprovides aligned icon/title/action chrome through--modal-header-bg; modal body wrappers expose--modal-padand support.bleedbands.actual-css design(Bun) exports a theme to Figma and Penpot tokens; seedocs/pages/guides/design-tools.md.actual-css designwrites 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 --corsserves the repository to another origin, such as penpot.app loading a local plugin.--meter-tracksets the.metertrack, matching--progress-track.--close-bgsets the dialog corner.closebackground, so a dialog over a painted band can drop the default chip.--close-colorand--close-hover-colorset the X ink at rest and on hover, so a bare X can take a painted band's foreground.--close-hover-overlayroutes the.closehover/active wash (matching--btn-hover-overlay); the modal/drawer corner sets it transparent so only the ink changes.
Changed
dialog.modalis 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
.closethat is a direct child ofdialog.modal > headerreserves inline room for itself; previously only the wrapper<header>did, so a long chrome title could run under it. dialog.modalderives one--dialog-inner-radiusfor the body wrapper, the header band, and the.dialog-confirmationfooter, so an edge band keeps the panel corner instead of taking its own radius.- The
.dialog-confirmationaction band spreads a two-action decision to its edges, keeping a cancel action apart from the committing one. .dialog-confirmationcenters its leading icon on the title while keeping supporting copy in the same text column..btnand the.fileselector button take their borders out of the block padding, so they match.inputheight at every size instead of standing 2px taller outside a.join.tests/browser/control-geometry.test.jsmeasures controls outside a.join, whose stretch had hidden the mismatch.
0.11.0
Breaking changes
enhancementSelector()andhasEnhancement()are no longer exported fromactual-css/js/enhance; query[data-enhance~="name"]directly.registerCommands()drops thepreparecallback; writearia-controls,aria-haspopup, andaria-pressedon command buttons in the markup.registerCommands()andcommandSelector()throw on a name that is neither a nativecommandkeyword nor a custom--command.FormValidator.init()is removed; the module self-registers, andapplyEnhancement("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-cardshows 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 indocs/pages/forms/choice-cards.md..choice-cardis 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-sizedefaults to1.25emand also sets the width of the.choice-cardindicator column.--aura-widthis the whole painted.auraframe (the border, no padding);--aura-width: 3pxreproduces the former default frame..auradefaults to a thin lit line:--aura-width: 1pxand--aura-intensity: 0.4.
Added
--surface-opaqueand--surface-solid-fglet a theme paint translucent surfaces while rings, separators and inverse text stay opaque.report:theme-contrastreports the--surface-solid-fgon--surface-solidtext pair.scripts/utils/color.jsholds the WCAG contrast and OKLCH math for reports and browser tests;contrast()refuses translucent colors.glassdemo theme: translucent surfaces over a theme-painted backdrop, a stress test of the opaque surface roles.tests/browser/translucent-surface.test.jskeeps rings, separators, step markers, and inverse text opaque over translucent surfaces.--choice-card-gapsets the space between a.choice-cardindicator and its label.tests/browser/choice-card.test.jslocks 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-colorand--shimmer-highlight..rangemoves to a1.125remthumb on a0.625remtrack with adata-enhance="range"fill (--range-progress) and nameddatalistvalues (aria-valuetextplusoutputsync).--flyout-padgives rich flyout panels dialog air.tests/browser/control-geometry.test.jslocks 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 at1em.
Changed
--indicator-ring, the.avatar-stackring, the.rangethumb separator, segmented.meterseparators and.stepsmarkers read--surface-opaque..tooltiptext reads--surface-solid-fg..auramasks its frame under the child with--surface-opaque, so a transparent or translucent child is framed rather than filled.- The
.aurafar halo is an outerbox-shadow, so it never paints under the child. .status-barwithout an intent paints--surface-solid/--surface-solid-fginstead 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, beforehandle; handlers no longer cancel the click themselves. --focus-outline(the genericcurrentColorfocus fallback) is--focus-ring-widthwide instead of2 × --border-width: 3px instead of 4px underprefers-contrast: more.
Fixed
actual-css/js/rangesynchronizes fill,aria-valuetext, and outputs after a native form reset, including controls associated throughform="…", 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.htmlloads the shared composer recipe before local overrides so the bottom bar keeps its flat border and compact textarea..otpwith.inputon 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;Enterfollows the highlight instead of closing the dialog. .taband.ratingdraw the component focus ring (--focus-ring-widthsolid--focus): an intent on the rating no longer becomes the focus color, and neither ring is 4px underprefers-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.jsshows 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.jsasserts the chip's dot layer keeps its height, not just its clip and colour.tests/exports.test.jskeys its inventory on every JS subpath ofpackage.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 (
selectfinds the Combobox page viasearchable select) and matches body text on word boundaries, soselectionno longer outranksselect; Combobox and Forms carryselect,custom select, andform controlsaliases. focus.cssdocuments two focus contracts: text-like elements keep thecurrentColorfallback, boxed components draw the--focusring, and an invalid field's--form-invalid-borderring is the one exception.tests/css-audit.test.jsfails on any frameworkoutlineoutside the two focus contracts.report:theme-contrastreports each preset's invalid-field focus line against the field background.check:syncfails when the.smand.lgparticipant lists invariants.cssdiverge.- Tailwind guide:
.outlineis 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.chipis a selectable label (never nest a.closeinside 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
.tabsstrip /.tabtrigger pair is documented as the container/item convention, with thetabmodule versustabstoken split stated. - Pagination: items are documented as
.btn.ghostwith chevrons on Previous/Next, soaria-current="page"is the only filled item. - Rich text:
demo/templates/rich-text.htmlis now an@lekoala/rich-textintegration (@0.2from jsDelivr) instead of a hand-wired Squire prototype, with the skin reduced to a--rt-*token bridge plus a rich composer reusing.composerchrome.
0.10.0
Breaking changes
.invertedis removed: it mixed a contrasting surface with theme inversion while remapping only part of the palette. Use adata-themeisland for a complete light/dark subtree, or paint a local band in application CSS..white-space-normalis renamed.text-wrap, the counterpart of.text-nowrap..dialog-close,.drawer-close, and.alert-dismissare 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 > buttonis no longer styled, and an empty one no longer paints an X. --alert-dismiss-sizeand--badge-dismiss-icon-sizeare replaced by--close-size/--close-icon-size.- The dialog corner applies to a
.closethat is a direct child of the dialog, of itsformorheader, or of aforminside that header. getSurfaceAutoCloseis removed fromsurface.js: it had no caller, and the code that opens a surface passes its ownautoClose..column-span-1…12and.column-start-1…12are replaced by the--column-span/--column-starthooks on each.column-layoutchild.- The bare
actual-cssentrypoint resolves to the complete compiled framework, likeactual-css/full,style,unpkg, andjsdelivr; importactual-css/cssfor the core alone. package.json#mainis removed.dist/actual.cssanddist/actual.min.cssare no longer built or published; compose the core fromactual-css/css.dist/actual.js(the compiled loader alone) is no longer built or published; bundle your own entry fromactual-css/jsandactual-css/js/*..input,.textarea,.select, and an open custom select draw a--focus-ring-widthinset outline in--focusinstead of a focus border plus outer halo..joinno 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.colordraw a solid--focus-ring-widthline in--focusat--focus-outline-offsetinstead of a translucent halo..rangethumb focus ring is solid--focusinstead of--focus-ring.--focus-ringand--focus-ring-shadoware removed; every focus indicator reads--focus.--btn-focus-colorand--btn-focus-ring-colorare removed: a button's focus color no longer follows its intent..check,.radio, and.switchkeep their resting border on focus instead of switching it to--focus.- The default
--focusishsl(268 30% 55%)in both schemes instead ofvar(--neutral): a primary-hued mid-tone that holds 3:1 against--surfaceand--surface-solid. --focus-ring-widthis2pxinstead of3px, and3pxinstead of4pxunderprefers-contrast: more.bootstrap-v6sets--focus: #3d8bfdand--focus-ring-width: 3pxinstead of its own ring recipe.
Added
.bleedis a shared surface contract:src/css/components/bleed.cssreads the relayed--surface-pad, so a direct child of a.draweror of an accordion panel escapes its inset like a card child already did.--accordion-padhook: the summary row and the panel no longer hard-code their inset, and the panel relays it so.accordion.separateditems accept a.bleedchild.--accordion-marker-sizehook..data-list.stacked: the value sits under its term instead of beside it.--bleed-padis a public read-only hook:padding: var(--bleed-pad)pads a.bleedband that is not a header, footer, or figure..joinsupports a control nested one wrapper level deep (e.g. a combobox element): the wrapper carries the group corners and primary-edgez-index, the inner control inherits the radius, and no widget is named..nav-linkworks on a<button>: UA button chrome is neutralized with<a>-identical metrics.materialdemo 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-dotis a read-only hook on the chip's::before: the check-slot centre, so a theme can grow the dot'scircle()clip into a selected fill.- The
materialtheme grows the chip dot into a tonal fill. - The
gradienttheme demos the outer-halo field focus;bootstrap-v6andedgehave no field focus override. demo/templates/contrast-contexts.html: the page,data-themeislands, 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.joingallery with no focus adaptation.
Fixed
dialog.jsonly backdrop-dismisses when the press started on the backdrop: a text selection released over the backdrop no longer closes (or shakes) the dialog.dialog.jsrestores 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.jsignores composition too.status.jsrestores its bar when the host dialog was removed from the DOM (htmx/Turbo swap): the nextstatus()heals instead of no-opping forever.tooltip.jsresynchronizes shorthanddata-tooltiptext 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.jssetsaria-haspopup="menu"only for a truerole="menu"panel:<menu>carries the implicit list role.dialog.jsrespectsclosedby="none"oncancelwhenHTMLDialogElement.closedByis unavailable, while explicit close requests still work.dialog.jsrestores generated ARIA attributes, title IDs, and rewrittenclosedbyvalues on disconnect without overwriting author changes.- A last
.bleedchild of an accordion panel had square corners over the rounded.separateditem or the group's last item. applyEnhancement("validation", …)did not wire an already-connected form:validation.jsnow registers throughregisterEnhancement.--app-nav-side-sizewas invisible tocheck:css-api(header readPublic hook:).- A custom
data-themeon<html>kept the default palette's--*-soft-fgcalibration instead of resetting it, so the same theme resolved a different soft ink on<html data-theme>than on a nested island. .nav-linkhover/current and.app-navhover fill with a tint of their own ink and follow--ui-fg, so they read on a bar with its own--ui-bg..flyoutownscolor: var(--text), like modal and drawer.- Preset themes: every
--focusis a mid-tone that holds 3:1 on--surfaceand--surface-solid, and a--soft-fg-mixkeeps every soft ink at 4.5:1 (report:theme-contrastlists no failing pair). report:theme-contrastmeasures.badge.softexplicitly, so a preset that fills badges by default (bootstrap-v6) is no longer reported against a solid pair.oceantheme: light--dangerdarkened (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--focusthat holds 3:1 on both surfaces..join-addonhonours--control-border-block-end-color/-widthlike text controls, so a theme's underline or accent edge reaches the addon segment.gradienttheme: the focus halo follows validation (--form-invalid-border), and it andmaterialrestyle the open customizable select with their field focus instead of the core:openoutline.--dialog-focus-bleedand--drawer-focus-bleedreservemax(--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
.joinstacks above an adjacent field (z-index: 4rule now follows the primary-edge rule), so the neighbour no longer covers its shared edge. - A field wrapped one level deep in
.joinfills its stretched wrapper (block-size: 100%) instead of stopping short of a taller sibling. - Focus indicators on a
--surface-solidband 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/combobox0.5.0, replaces its halo with the field inset outline, reads--form-invalid-border, and inherits.joincorners. tests/browser/field-focus.test.jsasserts the field outline stays inside the border box (default andprefers-contrast) and that--focusholds 3:1 on--surfaceand--surface-solidin both schemes.bun run report:theme-contrastalso reports each preset's--focusagainst--surfaceand--surface-solid.- Theming: "Make a dark (or light) section" replaces the
.invertedrecipe — adata-themeisland 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.htmlis removed;contrast-contexts.htmlcovers surface boundaries.tests/browser/inverted.test.jsbecomessurface-context.test.js, on an application-painted band.- Pushing a version tag publishes the GitHub release:
.npmrckeepsnpm versionon the bare tag format,bun run release:notesextracts (and validates) the dated changelog section, and the CIreleasejob attachesdist/. - 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...