cssints 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/check.mjs +1 -1
- package/dist/clay.mjs +3 -3
- package/dist/groups/css-anchor-position-1.d.mts +1 -1
- package/dist/groups/css-anchor-position-2.d.mts +1 -1
- package/dist/groups/css-animations-1.d.mts +1 -1
- package/dist/groups/css-animations-2.d.mts +1 -1
- package/dist/groups/css-conditional-3.d.mts +1 -1
- package/dist/groups/css-conditional-4.d.mts +1 -1
- package/dist/groups/css-conditional-5.d.mts +1 -1
- package/dist/groups/css-round-display-1.d.mts +1 -1
- package/dist/groups/css2.d.mts +1 -1
- package/dist/groups/mediaqueries-4.d.mts +1 -1
- package/dist/groups/mediaqueries-5.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/plugin.d.mts +1 -1
- package/dist/rolldown-runtime-BJohB4LM.mjs +13 -0
- package/dist/{scope-DzQLlev_.d.mts → scope-phC9Hofc.d.mts} +0 -1
- package/dist/surface.d.mts +1 -1
- package/dist/tui.mjs +4 -4
- package/dist/vite.mjs +2 -2
- package/docs/API.md +352 -66
- package/package.json +5 -5
package/dist/check.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { i as shown, r as ratio, t as luminance } from "./color-BB8ipynT.mjs";
|
|
1
2
|
import { s as ContrastError } from "./registry-8sYvlzyn.mjs";
|
|
2
3
|
import { t as fallbackOf } from "./lowered-DOJEDRFM.mjs";
|
|
3
|
-
import { i as shown, r as ratio, t as luminance } from "./color-BB8ipynT.mjs";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
// src/check.ts
|
|
6
6
|
/**
|
package/dist/clay.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as registry } from "./registry-8sYvlzyn.mjs";
|
|
2
|
-
import { a as createTokens$1, i as createGlobalTheme$1, j as fail, s as sets } from "./theme-BbKT-rP-.mjs";
|
|
3
|
-
import { n as valueOf, t as together } from "./contrast-BB0wimFI.mjs";
|
|
4
1
|
import { n as parseColor } from "./color-BB8ipynT.mjs";
|
|
2
|
+
import { n as valueOf, t as together } from "./contrast-BB0wimFI.mjs";
|
|
3
|
+
import { a as createTokens$1, i as createGlobalTheme$1, j as fail, s as sets } from "./theme-BbKT-rP-.mjs";
|
|
4
|
+
import { a as registry } from "./registry-8sYvlzyn.mjs";
|
|
5
5
|
import { Formatter, Predicate } from "effect";
|
|
6
6
|
import { clampRgb, formatHex } from "culori/fn";
|
|
7
7
|
// src/clay.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { a as GProp, n as GFn } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
5
5
|
type B = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { a as GProp, n as GFn, t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
5
5
|
type B = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, H as keyframes, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { a as GProp } from "../group-types-DrMdYewQ.mjs";
|
|
3
2
|
import "../group-common-BUdf_ry9.mjs";
|
|
3
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, H as keyframes, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
4
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
5
5
|
type B = {
|
|
6
6
|
id: "css-animations-1";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, H as keyframes, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { a as GProp } from "../group-types-DrMdYewQ.mjs";
|
|
3
2
|
import "../group-common-BUdf_ry9.mjs";
|
|
3
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, H as keyframes, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
4
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
5
5
|
type B = {
|
|
6
6
|
id: "css-animations-2";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
type B = {
|
|
5
5
|
id: "css-conditional-3";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
type B = {
|
|
5
5
|
id: "css-conditional-4";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { a as GProp, t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
5
5
|
type B = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
type B = {
|
|
5
5
|
id: "css-round-display-1";
|
package/dist/groups/css2.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { a as GProp, t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
5
5
|
type B = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
type B = {
|
|
5
5
|
id: "mediaqueries-4";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
2
1
|
import { t as GCondition } from "../group-types-DrMdYewQ.mjs";
|
|
2
|
+
import { A as ScvValue, B as global, C as CvValue, Dm as Fallback, E as PositionTry, F as cn, G as scv, K as style, M as Steps, N as Wrap, O as QueryWrap, Om as Part, P as attr, Q as ClassValue, R as cv, S as CvProps, V as group, Y as token, Z as within, _ as CSSStyleValue, a as DtcgUnit, c as Tokens, d as createTokens, f as layers, g as CSSImageValue, h as CSSColorValue, i as DtcgShadow, j as StateWrap, k as ScvConfig, km as Style, l as check, m as CSS, n as DtcgColor, o as DtcgValue, p as AttrOp, r as DtcgCubicBezier, s as Overrides, t as CheckOverrides, u as createGlobalTheme, v as CSSUnitValue, w as Keyframes, x as CvConfig, z as firstThatWorks } from "../theme-types-DRObPeMO.mjs";
|
|
3
3
|
import "../group-common-BUdf_ry9.mjs";
|
|
4
4
|
type B = {
|
|
5
5
|
id: "mediaqueries-5";
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as registry, d as MacroError, f as MemberError, o as warn, r as forgetKernels, t as Registry } from "./registry-8sYvlzyn.mjs";
|
|
2
2
|
import { A as camel, C as hashClass, D as settled, E as minWidth, F as probed, M as failIn, N as isNode, O as toDecls, P as node, S as edge, T as merged, _ as PSEUDOS, a as createTokens, b as checkVars, c as tokensOf, d as style, f as token, g as PROPS, h as LAYER, i as createGlobalTheme, j as fail, k as VAR, l as CSSTokenValue, m as FUNCTIONS, n as addTheme, o as isDone, p as CONTEXTS, r as check$1, t as DONE, u as isStyle, v as carried, w as isFallback, x as dropsOf, y as chainDecl } from "./theme-BbKT-rP-.mjs";
|
|
3
3
|
import { D as relation, S as addRuntime, _ as addKernel, i as byOrder, n as addGlobal, r as addLayers, y as kernelWarnings } from "./sheet-rwl57DmD.mjs";
|
|
4
|
-
import { n as loweredOf } from "./lowered-DOJEDRFM.mjs";
|
|
5
4
|
import { i as SCV_CALL, l as groups$1, n as CX_CALL, t as CV_CALL } from "./instrument-QGVM1E1B.mjs";
|
|
5
|
+
import { n as loweredOf } from "./lowered-DOJEDRFM.mjs";
|
|
6
6
|
import { checkDeclarationNow, checkQuery, checkSelector, checkValueNow, isDeclarationError } from "@cssints/css-grammar/check";
|
|
7
7
|
import { lackingOf, messageOf } from "@cssints/css-grammar/support";
|
|
8
8
|
import { CSS, CSSColorValue, CSSImageValue, CSSStyleValue, CSSStyleValue as CSSStyleValue$1, CSSUnitValue } from "@cssints/css-grammar/values";
|
package/dist/plugin.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as Style, S as Part, _ as fn, a as Entry, b as scope, c as Made, d as Scope, f as Table, g as dependsOn, h as arg, i as Css, l as Members, m as append, n as ArgsDef, o as Impl, p as Typed, r as Chain, s as KernelDef, u as Pending, v as inGroup, x as ship, y as lacking } from "./scope-
|
|
1
|
+
import { C as Style, S as Part, _ as fn, a as Entry, b as scope, c as Made, d as Scope, f as Table, g as dependsOn, h as arg, i as Css, l as Members, m as append, n as ArgsDef, o as Impl, p as Typed, r as Chain, s as KernelDef, u as Pending, v as inGroup, x as ship, y as lacking } from "./scope-phC9Hofc.mjs";
|
|
2
2
|
import { a as Kernel, c as ThemeRule, i as GlobalRule, n as Conflict, o as Runtime, r as Decl, s as Ship } from "./author-types-BNEUwM5s.mjs";
|
|
3
3
|
export { type ArgsDef, type Chain, type Conflict, type Css, type Decl, type Entry, type GlobalRule, type Impl, type Kernel, type KernelDef, type Made, type Members, type Part, type Pending, type Runtime, type Scope, type Ship, type Style, type Table, type ThemeRule, type Typed, append, arg, dependsOn, fn, inGroup, lacking, scope, ship };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
// \0rolldown/runtime.js
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __exportAll = (all, no_symbols) => {
|
|
5
|
+
let target = {};
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
11
|
+
return target;
|
|
12
|
+
};
|
|
13
|
+
export { __exportAll as t };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { r as Decl, s as Ship, t as BcdKey } from "./author-types-BNEUwM5s.mjs";
|
|
2
2
|
import { t as ClassValue } from "./runtime-Cv7hGrWf.mjs";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
3
|
import { CSS, CSS as CSS$1, CSSColorValue, CSSColorValue as CSSColorValue$1, CSSImageValue, CSSImageValue as CSSImageValue$1, CSSStyleValue, CSSStyleValue as CSSStyleValue$1, CSSUnitValue, CSSUnitValue as CSSUnitValue$1 } from "@cssints/css-grammar/values";
|
|
5
4
|
import { properties } from "@cssints/css-grammar/mdn";
|
|
6
5
|
import { DTCG_TYPES, DtcgType, Grammar, StyleObject, Syntax, TOKEN_TYPES, Token, TokenFn, TokenName, TokenSetFn, TokenType, ValidSyntax, ValidToken } from "@cssints/css-grammar/token";
|
package/dist/surface.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as AliasesOf } from "./scope-
|
|
1
|
+
import { t as AliasesOf } from "./scope-phC9Hofc.mjs";
|
|
2
2
|
/**
|
|
3
3
|
* `surface().color(t.color.canvas).lighten(0.06).ink()`: a background that follows from the surface around it, however
|
|
4
4
|
* many plain wrappers sit between. `color(<color>)` is an absolute base (default: the parent surface); `lighten(step)`,
|
package/dist/tui.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as registry } from "./registry-8sYvlzyn.mjs";
|
|
2
|
-
import { a as createTokens$1, i as createGlobalTheme$1, j as fail, s as sets } from "./theme-BbKT-rP-.mjs";
|
|
3
|
-
import { g as evaluate, h as css } from "./sheet-rwl57DmD.mjs";
|
|
4
|
-
import { n as valueOf, t as together } from "./contrast-BB0wimFI.mjs";
|
|
5
1
|
import { n as parseColor } from "./color-BB8ipynT.mjs";
|
|
2
|
+
import { n as valueOf, t as together } from "./contrast-BB0wimFI.mjs";
|
|
3
|
+
import { g as evaluate, h as css } from "./sheet-rwl57DmD.mjs";
|
|
4
|
+
import { a as createTokens$1, i as createGlobalTheme$1, j as fail, s as sets } from "./theme-BbKT-rP-.mjs";
|
|
5
|
+
import { a as registry } from "./registry-8sYvlzyn.mjs";
|
|
6
6
|
import { Formatter } from "effect";
|
|
7
7
|
import { clampRgb, formatHex, formatHex8 } from "culori/fn";
|
|
8
8
|
// src/tui.ts
|
package/dist/vite.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { r as verifyChecks } from "./contrast-BB0wimFI.mjs";
|
|
1
2
|
import { c as EvalError, d as MacroError, f as MemberError, i as forgetWarnings, l as HookError, n as forget, p as SiteMissingError, t as Registry } from "./registry-8sYvlzyn.mjs";
|
|
2
|
-
import { C as fallbackEntries, T as runtimeNames, a as closeNames, b as DERIVED, c as layerEntries, f as themedEntries, l as layerStatements, m as usesDerived, n as addGlobal, o as globalEntries, r as addLayers, s as globalNames, t as LAYERS, u as renderSheet, v as kernelEntries, w as runtimeModule, x as RUNTIME_ID } from "./sheet-rwl57DmD.mjs";
|
|
3
3
|
import { a as instrument, c as writeSite, o as runtimeImport, s as tagged } from "./instrument-QGVM1E1B.mjs";
|
|
4
|
-
import { r as verifyChecks } from "./contrast-BB0wimFI.mjs";
|
|
5
4
|
import { i as serverId, n as gitRoot, o as sessionOf, r as register } from "./devtools-link-DN91p7g5.mjs";
|
|
5
|
+
import { C as fallbackEntries, T as runtimeNames, a as closeNames, b as DERIVED, c as layerEntries, f as themedEntries, l as layerStatements, m as usesDerived, n as addGlobal, o as globalEntries, r as addLayers, s as globalNames, t as LAYERS, u as renderSheet, v as kernelEntries, w as runtimeModule, x as RUNTIME_ID } from "./sheet-rwl57DmD.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { targets as targets$1, useTargets } from "@cssints/css-grammar/support";
|
|
8
8
|
import { Cause, Clock, Config, Data, Effect, Exit, Formatter, Option, Predicate, Result, Schema, Semaphore } from "effect";
|
package/docs/API.md
CHANGED
|
@@ -1230,6 +1230,7 @@ More plugins on `scope()`, each a package of its own with its rules and limits i
|
|
|
1230
1230
|
| [`@cssints/layout`](../packages/layout/README.md) | `stack()`, `cluster()`, `sidebar()`, `switcher()`, `cover()`, `autoGrid()` | Every Layout primitives as chain members |
|
|
1231
1231
|
| [`@cssints/fonts`](../packages/fonts/README.md) | `font(family, { provider, ... })` → `.family()`, `.trim()` | `@font-face` and a metric-matched fallback face; cap-height trimming by `text-box`, else capsize's margins |
|
|
1232
1232
|
| [`@cssints/reset`](../packages/reset/README.md) | `reset({ media?, prose? })` | A modern reset in a top-level `@layer reset` declared before the engine's, below every style; `@cssints/reset/<baseline group>` keeps only the declarations of that group |
|
|
1233
|
+
| [`@cssints/data`](../packages/data/README.md) | `data()` | `max`, `sum` and `count` of runtime values, computed by layout and printed by a counter |
|
|
1233
1234
|
| [`@cssints/tokens`](../packages/tokens/README.md) | `/open-props`, `/radix`, `/flexoki`, `/reasonable-colors`, `/madoromi` | DTCG data for `createTokens` |
|
|
1234
1235
|
| [`@cssints/gradient`](../packages/gradient/README.md) | `gradient(kind, direction, from, to, easing?, stops?)` | Eased colour stops mixed in oklab at build time |
|
|
1235
1236
|
| [`@cssints/gradient-2d`](../packages/gradient-2d/README.md) | `meshGradient(mesh)`, `freeformGradient(points)` | `mesh-gradient()` / `freeform-gradient()` where the browser has them, else a runtime's canvas (this package's algorithms; lines and shapes not yet) |
|
|
@@ -1242,6 +1243,7 @@ More plugins on `scope()`, each a package of its own with its rules and limits i
|
|
|
1242
1243
|
| [`@cssints/masonry`](../packages/masonry/README.md) | `masonry().columns().gap()` | `display: grid-lanes`, else CSS `columns` (top to bottom per column) |
|
|
1243
1244
|
| [`@cssints/placeholder`](../packages/placeholder/README.md) | `placeholder(path, { mode? })` | An `<image>` from a JPEG or PNG read at build time: a gradient of its colours laid out as the image, or a tiny thumbhash PNG |
|
|
1244
1245
|
| [`@cssints/ramp`](../packages/ramp/README.md) | `ramp(base, { mode, steps \| ratios, background })`, `overrides(ramp)` | An OKLCH palette as DTCG data for `createTokens`; the contrast mode meets each ratio against its background |
|
|
1246
|
+
| [`@cssints/gamut`](../packages/gamut/README.md) | `fit(color, { target?, method? })`, `readable(bg, { ratio?, toward? })`, `tones(color, count)` | Gamut and contrast maths at build time: the nearest colour sRGB or Display P3 can show, a text colour that meets a WCAG 2 ratio, and a tonal scale as DTCG data for `createTokens` |
|
|
1245
1247
|
| [`@cssints/rybitten`](../packages/rybitten/README.md) | `color(hue, { cube })`, `wheel(steps, { cube })`, `shades(hue, lightness)`, `overrides(group)` | RYBitten's red-yellow-blue colours (36 historical cubes) as DTCG data for `createTokens`: a hue, a wheel of hues, a hue at several lightnesses |
|
|
1246
1248
|
| [`@cssints/skeleton`](../packages/skeleton/README.md) | `skeleton().radius().color().highlight()` | Loading placeholders whose shimmer is in phase across the page (one clock on `:root`), static under reduced motion |
|
|
1247
1249
|
| [`@cssints/reactive`](../packages/reactive/README.md) | `state()`, `toggle()`, `set()`, `value()`, `checked()`, `persist()`, `equals()`, `either()`, `on()`, `emit()`, `bind()`, `when()`, `inside()` | State as custom properties, events as data on the element: writes only in events, conditions and DOM output in plain CSS, wired by a 1.4 kB runtime shipped to every target; `value()`/`checked()` take the field's value, calls of `on()`, `bind()` or `state()` in one `cn()` append, `persist(value, key)` keeps a state across a reload, and `bind()` takes a source, a comparison (`equals()`), a choice of two literals (`either()`) or a literal — the one expression the runtime evaluates, because an attribute on the element cannot be computed in CSS (2026-10-31, cssints-ac3o). `emit(name)` is the one hand-off to JavaScript: `on({ submit: emit("save") })` writes no state and dispatches a bubbling `reactive:save` from the element the native event fired on, with `{ event, owner }` in its detail, which `listen(target, name, handler)` from `@cssints/reactive/runtime` hears and whose disposer it returns (2026-10-31, cssints-4s8d) |
|
|
@@ -1281,7 +1283,7 @@ createMachineStore(machine, { settled?, intent? }): { state, optimistic, project
|
|
|
1281
1283
|
- **Debounce** coalesces a burst by event type; `flush()` delivers the final intent, `cancel(type)` drops a superseded one. The same clock drives `after` and a transition's timestamp.
|
|
1282
1284
|
- **The clock is injected** (`systemClock` is the default); the harness's fake clock moves only on `advance(ms)`, and the harness logs `{ at, event, from, to, effect, context, cancelled }` per delivery (and per effect it issued).
|
|
1283
1285
|
- **The state is an external store**: `getState()`, `subscribe(listener)` and `subscribe(selector, listener)`, the last one firing only when the slice changes.
|
|
1284
|
-
- **`./solid`**: `state` is a `createStore` of `{ ...context, phase }` written once per transition, mutating only the changed fields; `optimistic` is a
|
|
1286
|
+
- **`./solid`**: `state` is a `createStore` of `{ ...context, phase }` written once per transition, mutating only the changed fields; `optimistic` is a second store over the same fields that follows `state` and carries an intent's phase on top, dropped when a transition settles (a superseded intent rolls back); `projection` is a `createProjection` over `state`, so a reader of a view's field does not re-run when another field changes. A projection reads the committed frame, not an optimistic override, so the visual phase is read off `optimistic` directly. One store write per transition is the atomicity the reactive runtime gets from its one flush (cssints-tm9j).
|
|
1285
1287
|
|
|
1286
1288
|
Limits: one state at a time (no parallel states, history states or nested machines), effects are data the host runs, and a numeric event `id` is reserved for an effect handle. There is no Quint spec for this runtime yet; the harness is the oracle.
|
|
1287
1289
|
|
|
@@ -1512,9 +1514,76 @@ createTable(options: {
|
|
|
1512
1514
|
|
|
1513
1515
|
`rows` may be a getter (`() => rows()`): it is read at attribute-read time, so a table whose rows arrive later keeps its sort and its selection instead of being rebuilt, and a selection made before the rows landed applies once they do. Selection is over ids, so it survives the host's re-sort. `rowProps` carries `aria-selected` and a click that selects; `toggleRow` / `toggleAll` are the programmatic path, and the row's checkbox stops its click from reaching the row so the two controls never toggle the same row twice. `selectAllProps` and `rowCheckboxProps` are `undefined` unless `selection` is `"multiple"` (one control over one selection is meaningless), and the select-all is a `role="checkbox"` rather than a native `input` because the mixed state a screen reader must hear is `aria-checked="mixed"`, not the DOM-only `indeterminate` property. It toggles the rows the table renders: a windowed table toggles its window and keeps the rest of the selection. A table that renders all of its rows and columns carries no `aria-rowcount` / `aria-colcount` (the markup already says it); pass them, and every cell gets its `aria-colindex`, only when the host windows. `numeric` is a layout hint for the host's skin: HTML has no attribute for it.
|
|
1514
1516
|
|
|
1517
|
+
### Headless: `createGrid` (`@cssints/headless`)
|
|
1518
|
+
|
|
1519
|
+
The APG grid as state and attribute objects: one tab stop, 2-D navigation, a cell/row/column selection rectangle, sortable column headers, edit mode and a windowed body. `specs/apg/grid.qnt` (navigation/edit) is its model, with `grid-selection.qnt` and `grid-sort.qnt` for the two values; `packages/headless/test/grid.browser.test.tsx` replays and walks them in Chromium.
|
|
1520
|
+
|
|
1521
|
+
**The body is the platform's.** Movement and the roving tab stop are the V2 `focusgroup="grid"` contract (`focusGrid` below, `specs/patterns/focusgrid.qnt`): `gridProps.ref` builds the runtime, the owner's `focusgroup` attribute is rendered from the same definition, and arrows on both axes, Home/End (the row's ends), Ctrl+Home/End (the grid's), the edges and the last-focused memory are not this primitive's code. `row` / `col` are readings of the DOM — the owner's `focusin` maps the focused element back to its coordinate — and the host mirrors programmatic moves (`focus`, `clickCell`, PageUp/PageDown, Shift+Arrow) into DOM focus, as the test fixture does. The library keeps edit mode (`Enter` / `F2` / `a` / `Escape`, `lastEdit`), selection, sort, PageUp/PageDown (a grid defines no page movement, so the runtime never sees those keys), the mount window and the `aria-*` indices.
|
|
1522
|
+
|
|
1523
|
+
**Wrapping is the platform's `flow`, not `wrap`.** `wrapCols` means "Right on the last column continues into the next row's first cell", which APG's layout-grid example writes `data-wrap-cols`; the runtime spells that `flow` (continue into the adjacent row, wrapping at the outer edge). `wrapRows` is the block axis the same way. A data grid is hard on both axes, so the definition is `{ rowEdge: wrapCols ? "flow" : "none", colEdge: wrapRows ? "flow" : "none", memory: true }`; `setWrapCols`, `setWrapRows` and `setGridKind` re-apply it through the runtime.
|
|
1524
|
+
|
|
1525
|
+
**The header row is still the library's and outside the group.** Sortable column headers keep their own one-dimensional roving, and the APG grid deliberately omits Up/Down between the header row and the body; `headerProps` marks each header `focusgroup="none"` so neither provider pulls it into the body's item collection. Tab then walks from the header stop into the body stop, as two stops of the sequential order.
|
|
1526
|
+
|
|
1527
|
+
**The `tabindex` recipe.** Each cell target carries a constant author `tabindex="0"` (the native engine navigates only items with `tabIndex > -1`, and a `<div role="gridcell">` needs an author value to be focusable at all) and the runtime writes the single stop over it. With `focusTarget: "widget"` the widgets are the item set, so cells author `-1` and widgets `0`. A host must not bind `tabindex` reactively after these props. `focusgroupstart` rides on the current coordinate; with `memory: true` the platform prefers its own memory once focus has been inside, so the marker is what the first Tab entry lands on. `aria-activedescendant` is still emitted for a widget target: it is an announcement, while focus really sits on the widget.
|
|
1528
|
+
|
|
1529
|
+
```ts skip
|
|
1530
|
+
createGrid(options?: {
|
|
1531
|
+
rows?: number; // 4
|
|
1532
|
+
cols?: number; // 3
|
|
1533
|
+
gridKind?: "data" | "layout"; // "data"
|
|
1534
|
+
wrapCols?: boolean; // layout only: Right/Left flow across rows (data-wrap-cols)
|
|
1535
|
+
wrapRows?: boolean; // layout only: Down/Up flow across columns (data-wrap-rows)
|
|
1536
|
+
focusTarget?: "cell" | "widget"; // "cell"
|
|
1537
|
+
gridReadonly?: boolean;
|
|
1538
|
+
selectable?: boolean; // aria-selected + the selection keys
|
|
1539
|
+
sortEnabled?: boolean; // sortable columnheaders (aria-sort)
|
|
1540
|
+
value?: number | undefined; // controlled packed selection (packSelection); 0 = nothing
|
|
1541
|
+
defaultValue?: number;
|
|
1542
|
+
onValueChange?(value: number): void;
|
|
1543
|
+
sortValue?: number | undefined; // controlled packed sort (packSort); -1 = unsorted
|
|
1544
|
+
defaultSortValue?: number;
|
|
1545
|
+
onSortChange?(value: number): void;
|
|
1546
|
+
page?: number; // PageUp/PageDown step in rows, 2
|
|
1547
|
+
id?: string;
|
|
1548
|
+
rowWindow?: { start: number; count: number } | undefined; // the mounted row range
|
|
1549
|
+
provider?: "auto" | "polyfill"; // "polyfill" forces focusGrid's fallback
|
|
1550
|
+
}): {
|
|
1551
|
+
rows(): number; cols(): number;
|
|
1552
|
+
row(): number; col(): number; // readings of the DOM focus
|
|
1553
|
+
hasFocus(): boolean; mode(): "navigate" | "edit";
|
|
1554
|
+
gridKind(): "data" | "layout"; wrapCols(): boolean; wrapRows(): boolean;
|
|
1555
|
+
focusTarget(): "cell" | "widget"; gridReadonly(): boolean;
|
|
1556
|
+
lastEdit(): "none" | "committed" | "cancelled";
|
|
1557
|
+
renderedRows(): ReadonlyArray<number>;
|
|
1558
|
+
isSelected(row: number, col: number): boolean;
|
|
1559
|
+
selection(): number; sort(): number;
|
|
1560
|
+
sortColumn(): number; sortDirection(): "none" | "ascending" | "descending";
|
|
1561
|
+
focusTargetPart(): string | null; // the data-part the host mirror focuses
|
|
1562
|
+
currentCellId(): string; // the cell carrying focusgroupstart
|
|
1563
|
+
gridProps: { role: "grid"; "aria-rowcount"; "aria-colcount"; "aria-readonly"; "aria-activedescendant";
|
|
1564
|
+
focusgroup: string; ref(element: HTMLElement): void; onFocusOut(event: FocusEvent): void };
|
|
1565
|
+
rowProps(row: number): { role: "row"; id; "aria-rowindex" };
|
|
1566
|
+
cellProps(row: number, col: number): { role: "gridcell"; id; tabindex: number; focusgroupstart: "" | undefined;
|
|
1567
|
+
ref(element: HTMLElement): void; "aria-rowindex"; "aria-colindex"; "aria-readonly"; "aria-selected";
|
|
1568
|
+
onClick(): void };
|
|
1569
|
+
widgetProps(row: number, col: number): { tabindex: number; focusgroupstart: "" | undefined;
|
|
1570
|
+
ref(element: HTMLElement): void };
|
|
1571
|
+
headerProps(col: number): { role: "columnheader"; id; focusgroup: "none"; tabindex; "aria-sort";
|
|
1572
|
+
onClick(): void; onFocus(): void; onKeyDown(event: KeyboardEvent): void };
|
|
1573
|
+
focus(): void; blur(): void;
|
|
1574
|
+
clickCell(row: number, col: number): void;
|
|
1575
|
+
setGridKind(kind: "data" | "layout"): void;
|
|
1576
|
+
setWrapCols(flag: boolean): void; setWrapRows(flag: boolean): void;
|
|
1577
|
+
setFocusTarget(target: "cell" | "widget"): void;
|
|
1578
|
+
setGridReadonly(flag: boolean): void; setSelectable(flag: boolean): void; setSortEnabled(flag: boolean): void;
|
|
1579
|
+
};
|
|
1580
|
+
```
|
|
1581
|
+
|
|
1582
|
+
The selection and the sort are requests: uncontrolled by default (`defaultValue` / `defaultSortValue`), and a controlled host owns them with `value` / `sortValue`; `packSelection` and `packSort` are the injective values. `page` is the spec's PAGE constant: PageDown moves a page of rows and clamps to the last row, PageUp the same upward. A `rowWindow` is only the mount window — cell/row indices stay absolute — and the runtime re-collects when it moves, so the stop stays on a mounted cell or is re-seated on the first one. Edit mode is the spec's: a read-only grid and the last column never enter it, leaving commits, Escape cancels.
|
|
1583
|
+
|
|
1515
1584
|
### Headless: `createTabs` (`@cssints/headless`)
|
|
1516
1585
|
|
|
1517
|
-
The APG tablist as state and attribute objects:
|
|
1586
|
+
The APG tablist as state and attribute objects: the platform's single tab stop, arrows, Home/End and wrap; automatic or manual activation, disabled tabs, and a selection that is a request (`value` + `onValueChange`) or uncontrolled (`defaultSelected`). `packages/apg-specs/specs/apg/tabs.qnt` is its model (it imports `shared/focusgroup.qnt`); `packages/headless/test/tabs.browser.test.tsx` replays it in Chromium, on both providers.
|
|
1518
1587
|
|
|
1519
1588
|
Its accessibility requirements are catalogued, gap by gap, in `packages/headless/docs/requirements/tabs.md` (the model is `packages/headless/docs/requirements/README.md`).
|
|
1520
1589
|
|
|
@@ -1523,6 +1592,7 @@ Close and rename are an **extension** (bean `cssints-gdv2`, the docs playground'
|
|
|
1523
1592
|
```ts skip
|
|
1524
1593
|
createTabs(options?: {
|
|
1525
1594
|
...; // the plain tabs: items, value, defaultSelected, orientation, dir, activation, disabled, focusablePanels, id, tabId, panelId, onValueChange
|
|
1595
|
+
provider?: "auto" | "polyfill"; // "polyfill" forces @microsoft/focusgroup-polyfill even where the engine has focusgroup
|
|
1526
1596
|
closable?: boolean; // false: closeProps and the Delete key; the host removes from onClose
|
|
1527
1597
|
renamable?: boolean; // false: a double click edits the label inline; the host renames from onRename
|
|
1528
1598
|
label?(index: number): string | undefined; // the tab's name: the buttons' labels and the rename draft
|
|
@@ -1532,6 +1602,8 @@ createTabs(options?: {
|
|
|
1532
1602
|
...;
|
|
1533
1603
|
editing(): number | null; // the tab being renamed inline
|
|
1534
1604
|
focusAfterClose(index: number): number; // the tab to focus once the removal landed
|
|
1605
|
+
tablistProps: { role: "tablist"; "aria-orientation"?: "vertical"; focusgroup: string; dir?: "ltr" | "rtl"; ref };
|
|
1606
|
+
tabProps(index): { id; role: "tab"; "aria-selected"; "aria-controls"; "aria-disabled"; focusgroupstart; tabindex?: -1; ref; onClick; onMouseDown; onFocus; onKeyDown };
|
|
1535
1607
|
closeProps(index): { "aria-label": string; tabindex: -1; onClick: () => void };
|
|
1536
1608
|
renameProps(index): {
|
|
1537
1609
|
onDblClick: () => void;
|
|
@@ -1544,12 +1616,15 @@ createTabs(options?: {
|
|
|
1544
1616
|
};
|
|
1545
1617
|
```
|
|
1546
1618
|
|
|
1547
|
-
- **
|
|
1548
|
-
- **
|
|
1619
|
+
- **The platform's half.** `tablistProps` carries the `focusgroup` attribute (`tablist nomemory wrap`, `block` when vertical) and a `ref` that builds the provider, so arrows along the axis, Home / End, wrap and the single tab stop are the browser's (Chromium 150+) or `@microsoft/focusgroup-polyfill`'s. The definition is **no memory**: the group is entered on the selected tab (`focusgroupstart`, emitted by `tabProps`), not on wherever focus was last. `tabProps` therefore renders **no roving `tabindex`** — the provider owns it; the only `tabindex` a tab carries is a static `-1` on a disabled tab, which is how both providers are kept out of it. A host whose tab is not natively focusable (a `<div role="tab">` holding the rename input, say) gives it its own `tabindex="0"`; the platform still keeps a single stop among them. `provider: "polyfill"` skips the feature detect (for a test, or a page that needs the fallback); `focus` is a reading of the DOM, not a second cursor.
|
|
1620
|
+
- **The library's half.** Selection is the delta: automatic activation selects on the `focusin` a move causes, manual activation only on Enter / Space, and a click selects in both modes. A click on an already-focused tab selects itself (no `focusin` fires); a pointer press that does move focus is asked once, so a controlled host is not asked twice. Selection and focus are decoupled when a controlled host rejects a request — the tab stop then stays with the user's focus, which is why `tabs.qnt` keeps `tabStop` beside `selected`.
|
|
1621
|
+
- **Close.** `closeProps(index)` is the tab's close button: `aria-label="Close <label>"` and `tabindex=-1`, because a tablist's tab stops are its tabs; the keyboard path is Delete on the focused tab. It is not an item of the group either, so the platform neither moves focus to it nor acts on its keys. Focus after a close goes to the tab that slid into the closed one's place, or to the previous tab when the last one closed: `focusAfterClose(index)` names that index in the shortened list (call it before the host removes the tab). Closing the last remaining tab is refused, as the spec refuses an empty tablist; a closed tab that was selected asks for the tab that takes the focus.
|
|
1622
|
+
- **Rename.** A double click begins; the tab renders `inputProps` instead of its label. Enter commits, Escape reverts, blur commits, and a label that trims to nothing cancels. While editing, the input owns the keys: the tab's handler ignores them (and the platform already leaves a native `<input>`'s arrows alone). `begin(index)` is the programmatic start, for a tab the host has just added (append it, then `focusTab(index)` and `begin(index)`).
|
|
1549
1623
|
- **Ids.** `tabId(index)` and `panelId(index)` replace the generated `${id}-tab-<index>` / `${id}-tabpanel-<index>`, and `aria-controls` / `aria-labelledby` follow them. Use them when the host's markup or a deep link already names a panel (`#logs`): the alternative is a second id on the same element, which no attribute can carry.
|
|
1550
|
-
- **Direction.** `dir: "rtl"`
|
|
1551
|
-
- **The host's list.** `items` is read from the option on every read, so the bounds, the
|
|
1624
|
+
- **Direction.** `dir: "rtl"` renders as `dir` on `tablistProps`: the platform maps ArrowLeft / ArrowRight per the element's direction, so a horizontal rtl tablist puts the next tab to the left and a vertical one keeps Up / Down. The model `tabs.qnt` is ltr only, so the ported `direction` scenarios stay skipped; `packages/headless/test/tabs-requirements.browser.test.tsx` holds the swap.
|
|
1625
|
+
- **The host's list.** `items` is read from the option on every read, so the bounds, the entry point and `firstEnabled` follow a host that shrinks its list, and the primitive re-reconciles the provider on a selection or count change (the native engine reads `focusgroupstart` itself). `label` is read lazily too, so a renamed tab renames its buttons with it.
|
|
1552
1626
|
- The spec models a `deletable` flavour (Delete, focus to the next tab, the last refused) but neither a rename nor the callbacks, so the browser test, not the model, holds the extension.
|
|
1627
|
+
- **Migration (0.x, `minor`).** `TabProps` no longer carries the roving `tabindex` (spread `tabProps(i)` and bind none; a non-button tab needs its own `tabindex="0"`), and `Tabs.keyDown` is gone. `TablistProps` gained `focusgroup`, `ref` and `dir`, and `TabProps` gained `focusgroupstart` and `ref`.
|
|
1553
1628
|
|
|
1554
1629
|
### Headless: `createHotkeys` (`@cssints/headless`)
|
|
1555
1630
|
|
|
@@ -1769,7 +1844,7 @@ createDatePicker(options: {
|
|
|
1769
1844
|
};
|
|
1770
1845
|
```
|
|
1771
1846
|
|
|
1772
|
-
Every date is an ISO `YYYY-MM-DD`, read as a UTC midnight; `month()` and `labels.month` use 0-11. `monthDays()` is **six weeks around the shown month**, so a row can start in the previous month and end in the next one: every cell is a real focusable date (a blank cannot hold the roving focus), and a cursor that leaves the month pages the grid to where it landed. `cellProps(iso)` marks the chosen day `aria-selected`, `today` `aria-current="date"`, a day outside `min`/`max` `aria-disabled`,
|
|
1847
|
+
Every date is an ISO `YYYY-MM-DD`, read as a UTC midnight; `month()` and `labels.month` use 0-11. `monthDays()` is **six weeks around the shown month**, so a row can start in the previous month and end in the next one: every cell is a real focusable date (a blank cannot hold the roving focus), and a cursor that leaves the month pages the grid to where it landed. `cellProps(iso)` marks the chosen day `aria-selected`, `today` `aria-current="date"`, a day outside `min`/`max` `aria-disabled`, gives `tabindex=0` to the roving day only and the grid's `focusgroupstart` to that same day: the selected day, else today when it is in the shown month, else the first day the bounds allow. `gridProps` is the grid's `role` / `aria-*` only — no `focusgroup` attribute and no `ref`: the picker owns its keyboard and its roving day, and the grid's `row` / `col` mirror the cursor so `cellProps` can name the current cell. `attach()` focuses that cell by id, not by `[tabindex="0"]` (which no longer names one element in a grid whose platform owns the stop). Keys on the focused day: ←/→ a day, ↑/↓ a week, Home/End the week's first/last day, PageUp/PageDown a month, Shift+PageUp/PageDown a year (the day clamps, 29 February included), Enter/Space selects and closes, Escape closes without changing the value, Tab leaves. The month arrows page the month and move the roving tab stop; the DOM focus stays on the arrow, so a host that wants the next key to land on a day moves it after the cells re-rendered (the kit's nav buttons do). `attach()` is the popover's light dismissal and focus return, plus focus into the roving day on open; `contentStyle()` is its placement — the same page-space `position: absolute` box `createPopover` gives, so a popup that escapes a positioned or clipping ancestor is placed by the initial containing block, not by the trigger's parent. The host renders the cells, the CSS and the trigger's visible text; a segmented text field is a host composition of `createField` + `createSpinbutton` over the same day numbers, not part of this API.
|
|
1773
1848
|
|
|
1774
1849
|
### Headless: `createPanZoom` (`@cssints/headless`)
|
|
1775
1850
|
|
|
@@ -1822,6 +1897,14 @@ The host owns the box and the look: the viewport's size and `overflow: hidden`,
|
|
|
1822
1897
|
|
|
1823
1898
|
The APG menu family (`createMenu` for the menu button and the checkable menus, `createMenubar` for the roving bar) as state and attribute objects. Its behaviour is `specs/apg/menu-button.qnt`, `menu-checkable.qnt` and `menu-menubar.qnt`, and `packages/headless/test/menu-button.browser.test.tsx` and `packages/headless/test/menu.browser.test.tsx` replay the generated models, ported scenarios and seeded differential walks against it.
|
|
1824
1899
|
|
|
1900
|
+
The movement is the platform's `focusGroup` (`shared/focusgroup.qnt`, `./focusgroup`), one segment per level: the
|
|
1901
|
+
list is `focusgroup="menu"` (block axis, wrap), the bar is `focusgroup="menubar"` (the declared orientation and
|
|
1902
|
+
`wrap`), and each submenu is its own `menu` segment — the platform's contract is per segment, and a popup's list
|
|
1903
|
+
is a segment only while the popup is shown, so a closed one is suspended with `focusgroup="none"`. The arrows
|
|
1904
|
+
along each level's axis, Home / End, the wrap and the last-focused memory are the browser's (Chromium 150+) or
|
|
1905
|
+
`@microsoft/focusgroup-polyfill`'s, and **the platform owns the item `tabindex`**: the primitive never renders
|
|
1906
|
+
one. `provider: "polyfill"` forces the fallback (tests, hosts without the native attribute).
|
|
1907
|
+
|
|
1825
1908
|
```ts skip
|
|
1826
1909
|
createMenu(options: {
|
|
1827
1910
|
items: ReadonlyArray<string | { label: string; kind?: "item" | "checkbox" | "radio"; checked?: boolean; disabled?: boolean }>;
|
|
@@ -1839,6 +1922,7 @@ createMenu(options: {
|
|
|
1839
1922
|
disabled?: ReadonlyArray<number>; // focusable, never activated
|
|
1840
1923
|
id?: string | undefined;
|
|
1841
1924
|
onItemClick?(index: number): void;
|
|
1925
|
+
provider?: "auto" | "polyfill"; // "auto": the native attribute where the engine has it
|
|
1842
1926
|
}): {
|
|
1843
1927
|
open(): boolean;
|
|
1844
1928
|
focusLevel(): 0 | 1 | 2; // 0 outside, 1 on the trigger, 2 on an item
|
|
@@ -1848,12 +1932,44 @@ createMenu(options: {
|
|
|
1848
1932
|
value(): number; // the packed view of the same state
|
|
1849
1933
|
openMenu(): void; // a request, like every intent
|
|
1850
1934
|
closeMenu(): void;
|
|
1851
|
-
keyDown(key: string): void;
|
|
1935
|
+
keyDown(key: string): void; // the library's keys: Enter / Space, Escape, Tab
|
|
1852
1936
|
// …items, triggerProps, menuProps, itemProps(index), type(), setFocusItem()
|
|
1853
1937
|
};
|
|
1854
1938
|
```
|
|
1855
1939
|
|
|
1856
|
-
**The
|
|
1940
|
+
- **The list is the owner.** `menuProps` carries `role="menu"`, `hidden`, the definition as the `focusgroup`
|
|
1941
|
+
attribute (`"menu"` while shown, `"none"` while hidden) and the `ref` that builds the provider and registers
|
|
1942
|
+
the keydown listener **before** it — that order is what lets the primitive consume a key the platform would
|
|
1943
|
+
otherwise act on, and a delegated Solid `onKeyDown` would run at the document, after focus had moved. The row
|
|
1944
|
+
props carry no `tabindex` and no key handler: `onFocus` is how the primitive hears where the platform put the
|
|
1945
|
+
focus.
|
|
1946
|
+
- **A row must be focusable by nature.** The native engine writes no `tabindex` and only moves focus to an item
|
|
1947
|
+
that is tabbable already, so render a `<button>` or an `<a href>` per row (the polyfill decorates whatever the
|
|
1948
|
+
collection holds, and restores an authored `tabindex`). A `<div>` row works only on the polyfill.
|
|
1949
|
+
- **A test presses real keys.** The movement is the platform's, and a native engine ignores an untrusted
|
|
1950
|
+
`KeyboardEvent` dispatched from script: a test that presses an arrow declares it with `userEvent.keyboard`, or
|
|
1951
|
+
runs the widget on `provider: "polyfill"` (which is what `apps/ui/src/registry/menu/menu.browser.test.tsx`'s
|
|
1952
|
+
walks do, because their `tabindex` probe needs the rendered stop).
|
|
1953
|
+
- **It keeps the keys the platform does not infer**: Enter / Space activation, Escape, Tab (which closes and
|
|
1954
|
+
leaves), typeahead, and on a menubar the cross-axis keys — the open keys of the bar, `Right` into a submenu,
|
|
1955
|
+
`Left` back to the parent row or the previous bar item. The arrows along the axis, Home and End are not in
|
|
1956
|
+
`keyDown` and are never `preventDefault`ed by the primitive.
|
|
1957
|
+
|
|
1958
|
+
`createMenubar` is the same contract per level: `menubarProps` (`role="menubar"`, `aria-orientation`, the
|
|
1959
|
+
`focusgroup` attribute and the `ref`), `barItemProps(i)`, `menuProps(i)` and `submenuProps(i, j)` for the popups,
|
|
1960
|
+
`menuItemProps(i, j)` and `submenuItemProps(i, j, k)` for the rows. A vertical bar's own axis is the platform's
|
|
1961
|
+
too (`focusgroup="menubar block"`): `Up` / `Down` move between bar items and its cross-axis pair opens the menu,
|
|
1962
|
+
the same code path as `Right` / `Left` on a horizontal one. `focusLevel` (1 the bar, 2 a top-level menu, 3 a
|
|
1963
|
+
submenu) and `focusIndex` are what a host moves the DOM focus to; reading them is reading the DOM's own focus,
|
|
1964
|
+
written back by `onFocus`.
|
|
1965
|
+
|
|
1966
|
+
**Migration, if you rendered the item props before:** drop `tabindex` from your row markup and spread
|
|
1967
|
+
`menuProps` (or `menubarProps` and each `menuProps(i)` / `submenuProps(i, j)`) on the owner, so its `ref` builds
|
|
1968
|
+
the provider; render focusable rows. The row props gained `ref` (the provider's item collection), and
|
|
1969
|
+
`menuProps` / `menubarProps` gained `focusgroup` and `ref` and lost `onKeyDown`; `keyDown(key)` now carries only
|
|
1970
|
+
the library's keys.
|
|
1971
|
+
|
|
1972
|
+
**The checked state has one store and two views.** The readable pair (`checked` + `choice`) is the store; `value` is derived from it as `menu-checkable.qnt`'s `checked(wrap, choice)` (one checkbox bit \* 5 + the radio). The pack has one checkbox bit, so only the readable pair can name a checkbox beyond item 0. A host may own either view; each has its request (`onCheckedValuesChange(checked, choice)` and `onCheckedValueChange(value)`), and where both views are given the readable pair wins. `defaultChecked` / `defaultChoice` start the pair and `defaultValue` the packed side.
|
|
1857
1973
|
|
|
1858
1974
|
**The open state is a request/commit pair**, like every other controlled primitive: `open` (+ `onOpenChange`) makes the host own it, `defaultOpen` starts it. Every intent that opens or closes — the trigger's click and its keys, Escape, an item's activation, Tab, an outside click in a kit, the programmatic `openMenu` / `closeMenu` — is a request, so a host that holds `open` keeps the menu open and the focus where it was; `menu-button.qnt` reverts the whole transition of a rejected request, focus included. A write the host makes on its own is not a request: it takes the focus to the first item when it opens and to the trigger when it closes, and a write of the value it already has moves nothing (no widget can observe it).
|
|
1859
1975
|
|
|
@@ -2483,13 +2599,19 @@ The APG Radio Group: one `radiogroup` with `role="radio"` items, where selection
|
|
|
2483
2599
|
behaviour is `packages/apg-specs/specs/apg/radio.qnt`, replayed by
|
|
2484
2600
|
`packages/headless/test/radio.browser.test.tsx` against the ported ariakit, base-ui and React Aria scenarios
|
|
2485
2601
|
and a differential walk in six configurations (unchecked, checked, disabled, `activedescendant`, and a
|
|
2486
|
-
controlled host that accepts or holds a request).
|
|
2487
|
-
that arrives later is still one widget), `defaultValue` is the
|
|
2488
|
-
`onValueChange` make the host own it.
|
|
2602
|
+
controlled host that accepts or holds a request). The same file proves the native path with real keys. `values`
|
|
2603
|
+
is the option list (a getter is fine, so a list that arrives later is still one widget), `defaultValue` is the
|
|
2604
|
+
uncontrolled check and `value` + `onValueChange` make the host own it.
|
|
2489
2605
|
|
|
2490
2606
|
`value` may be a getter, and then `undefined` means "nothing checked" as a controlled state: it is the bare
|
|
2491
2607
|
`undefined` prop that says uncontrolled.
|
|
2492
2608
|
|
|
2609
|
+
The roving flavor is the platform's `focusgroup="radiogroup"` contract (the browser's where it has one,
|
|
2610
|
+
`@microsoft/focusgroup-polyfill` otherwise, `provider: "polyfill"` forces the polyfill): both axes, Home / End,
|
|
2611
|
+
wrap and the item `tabindex` are its. The library keeps the deltas — selection follows focus, the entry point
|
|
2612
|
+
(`focusgroupstart`: the checked radio, else the first enabled one) and Space, which focusgroup does not
|
|
2613
|
+
provide.
|
|
2614
|
+
|
|
2493
2615
|
```ts skip
|
|
2494
2616
|
createRadioGroup<T>(options: {
|
|
2495
2617
|
values: ReadonlyArray<T> | (() => ReadonlyArray<T>); // one per radio
|
|
@@ -2498,21 +2620,21 @@ createRadioGroup<T>(options: {
|
|
|
2498
2620
|
flavor?: "roving" | "activedescendant"; // "roving"
|
|
2499
2621
|
disabled?: ReadonlyArray<number>; // indices never focused, checked or picked as the tab stop
|
|
2500
2622
|
orientation?: "horizontal" | "vertical"; // sets aria-orientation only
|
|
2501
|
-
dir?: "ltr" | "rtl"; //
|
|
2623
|
+
dir?: "ltr" | "rtl"; // forwarded to the DOM: the platform reads it for the horizontal arrows
|
|
2502
2624
|
id?: string; // base of `<id>-radio-<i>`
|
|
2503
2625
|
describedby?: string | undefined; // aria-describedby on every radio
|
|
2626
|
+
provider?: "auto" | "polyfill"; // "auto"; "polyfill" forces the polyfill (a test, or a host that needs it)
|
|
2504
2627
|
onValueChange?(value: T): void; // a request the host may ignore
|
|
2505
2628
|
onFocus?(target: string): void; // "radio:<index>"
|
|
2506
2629
|
onBlur?(target: string): void;
|
|
2507
2630
|
}): {
|
|
2508
2631
|
checked(): number | null; // the index, null when nothing is checked
|
|
2509
|
-
focused(): number | null;
|
|
2632
|
+
focused(): number | null; // follows the DOM's focusin
|
|
2510
2633
|
flavor(): RadioFlavor;
|
|
2511
2634
|
values: ReadonlyArray<T>;
|
|
2512
2635
|
value(): T | null;
|
|
2513
|
-
focus(): void;
|
|
2636
|
+
focus(): void; // moves the focus *state*; the host moves the DOM focus
|
|
2514
2637
|
blur(): void;
|
|
2515
|
-
keyDown(key: string): void;
|
|
2516
2638
|
click(index: number): void;
|
|
2517
2639
|
setDisabled(indices: ReadonlyArray<number>): void;
|
|
2518
2640
|
setFlavor(flavor: RadioFlavor): void;
|
|
@@ -2522,6 +2644,9 @@ createRadioGroup<T>(options: {
|
|
|
2522
2644
|
"aria-orientation";
|
|
2523
2645
|
"aria-activedescendant": string | undefined; // only the activedescendant flavor
|
|
2524
2646
|
tabindex: 0 | undefined; // 0 only in the activedescendant flavor
|
|
2647
|
+
focusgroup: string | undefined; // the roving definition ("radiogroup wrap nomemory"), absent otherwise
|
|
2648
|
+
dir: "ltr" | "rtl" | undefined; // the option, forwarded
|
|
2649
|
+
ref: (element: HTMLElement) => void; // builds the platform's provider
|
|
2525
2650
|
onKeyDown;
|
|
2526
2651
|
onFocus;
|
|
2527
2652
|
onBlur;
|
|
@@ -2533,21 +2658,37 @@ createRadioGroup<T>(options: {
|
|
|
2533
2658
|
"aria-disabled": "true" | "false";
|
|
2534
2659
|
"aria-describedby";
|
|
2535
2660
|
type: "button"; // keeps a surrounding form from submitting
|
|
2536
|
-
|
|
2661
|
+
focusgroupstart: "" | undefined; // roving: the platform's entry marker, on the checked radio
|
|
2662
|
+
focusgroup: "none" | undefined; // roving: opts a disabled radio out of the group
|
|
2663
|
+
tabindex: 0 | -1 | undefined; // the author value the platform needs; it owns the roving stop
|
|
2664
|
+
ref: (element: HTMLElement) => void;
|
|
2537
2665
|
onClick;
|
|
2538
2666
|
onFocus;
|
|
2539
2667
|
};
|
|
2540
2668
|
};
|
|
2541
2669
|
```
|
|
2542
2670
|
|
|
2543
|
-
- **Two flavors, one machine.** `"roving"` (the default)
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2671
|
+
- **Two flavors, one machine.** `"roving"` (the default) is the platform's tab stop: an arrow, Home or End
|
|
2672
|
+
moves DOM focus, the primitive never renders a roving `tabindex` (the polyfill writes `0` / `-1`; the native
|
|
2673
|
+
engine writes nothing and keeps the stop in its focus controller), and a focus that arrives from outside
|
|
2674
|
+
only moves the state. `"activedescendant"` puts the tab stop on the group (`tabindex=0` there, nothing on
|
|
2675
|
+
the radios) and names the focused radio in `aria-activedescendant`: focusgroup models DOM focus moving
|
|
2676
|
+
between items, so this flavor keeps its keyboard in the primitive.
|
|
2677
|
+
- **Selection follows the focus.** An arrow moves to the next enabled radio (wrapping) _and requests_ it —
|
|
2678
|
+
the `focusin` the platform causes is the library's, since focusgroup leaves selection to the pattern. Space
|
|
2679
|
+
requests the focused one, a click focuses and requests, and Tab enters on the checked radio. `setPreselect`
|
|
2550
2680
|
starts the check without a request, and `setDisabled` / `setFlavor` reset the focus.
|
|
2681
|
+
- **The tab stop is the platform's, and the entry point is the library's.** While focus is inside the group
|
|
2682
|
+
the stop is the focused radio — the native engine keeps it in its focus controller, the polyfill renders
|
|
2683
|
+
`tabindex=0` on it — and the primitive does not re-seat it, so a controlled host that rejects a request
|
|
2684
|
+
leaves the stop on the radio the user moved to. The definition is `nomemory` with `focusgroupstart` on the
|
|
2685
|
+
checked radio (else the first enabled one), so the next Tab entry — and the stop once focus leaves — is the
|
|
2686
|
+
checked radio: the rule users see, `radio.qnt` models the same way via `fgTabIndex` / `fgEntry` /
|
|
2687
|
+
`fgStopOnLeave`.
|
|
2688
|
+
- **A disabled radio is out of the group** (`focusgroup="none"`, `tabindex="-1"`): arrows and the tab stop
|
|
2689
|
+
skip it and Tab never lands on it. Against the native engine every other radio carries `tabindex="0"`,
|
|
2690
|
+
because that engine navigates only items with `tabIndex > -1` and a `<div role="radio">` is otherwise not
|
|
2691
|
+
focusable at all; the engine still gives the group one tab stop.
|
|
2551
2692
|
- **Config remounts fire no request.** `setDisabled`, `setFlavor` and `setPreselect` write the uncontrolled
|
|
2552
2693
|
local directly; with `value` set, what is checked is the prop's, so they only move the focus.
|
|
2553
2694
|
- **Two divergences:** React Aria renders native `<input type="radio">` elements and carries the HTML
|
|
@@ -2556,8 +2697,8 @@ createRadioGroup<T>(options: {
|
|
|
2556
2697
|
|
|
2557
2698
|
ArrowDown / ArrowRight are forward and ArrowUp / ArrowLeft backward, with `dir: "rtl"` swapping the
|
|
2558
2699
|
horizontal pair; both axes work whatever `orientation` says, since that option only sets `aria-orientation`.
|
|
2559
|
-
The arrows wrap past the ends and skip every disabled index; Home / End
|
|
2560
|
-
no such key.
|
|
2700
|
+
The arrows wrap past the ends and skip every disabled index; Home / End come from the platform, so the roving
|
|
2701
|
+
flavor handles them too (the spec has no such key).
|
|
2561
2702
|
|
|
2562
2703
|
### Headless: `createTooltipGroup` (`@cssints/headless`)
|
|
2563
2704
|
|
|
@@ -2644,21 +2785,135 @@ Escape on a trigger hides the tooltip (`escape()`) without moving the focus or t
|
|
|
2644
2785
|
reopens it until that trigger is left and entered again — WCAG's dismissable part. No other key is bound:
|
|
2645
2786
|
focus and hover are the other two ways in, and the pointer handlers above are the hoverable part.
|
|
2646
2787
|
|
|
2788
|
+
### Headless: `focusGroup` (`@cssints/headless`)
|
|
2789
|
+
|
|
2790
|
+
The platform layer of the `focusgroup` contract — one tab stop, arrows along the axis, Home / End, wrap and
|
|
2791
|
+
the last-focused memory — the behaviour `packages/apg-specs/shared/focusgroup.qnt` models once for every
|
|
2792
|
+
widget. Two providers behind one definition:
|
|
2793
|
+
|
|
2794
|
+
```ts skip
|
|
2795
|
+
focusGroup(owner: HTMLElement, options: {
|
|
2796
|
+
definition: {
|
|
2797
|
+
behavior: "toolbar" | "tablist" | "radiogroup" | "listbox" | "menu" | "menubar" | "none";
|
|
2798
|
+
wrap?: boolean; // false emits `nowrap`
|
|
2799
|
+
axis?: "inline" | "block"; // "block" for a vertical widget
|
|
2800
|
+
memory?: boolean; // false emits `nomemory`
|
|
2801
|
+
};
|
|
2802
|
+
items: () => ReadonlyArray<HTMLElement>; // read now and on every update()
|
|
2803
|
+
}): {
|
|
2804
|
+
attribute: string; // the definition as the focusgroup attribute
|
|
2805
|
+
update(definition?: definition): void;
|
|
2806
|
+
destroy(): void; // idempotent
|
|
2807
|
+
};
|
|
2808
|
+
|
|
2809
|
+
focusGroupPolyfill(owner, options); // the same, skipping the feature detect
|
|
2810
|
+
focusGroupAttribute(definition); // "toolbar block nowrap"
|
|
2811
|
+
supportsFocusGroup(); // 'focusGroup' in HTMLElement.prototype
|
|
2812
|
+
```
|
|
2813
|
+
|
|
2814
|
+
- **Native first.** Where `'focusGroup' in HTMLElement.prototype` (Chromium 150+), the adapter sets the
|
|
2815
|
+
owner's `focusgroup` attribute and stops there: the browser owns the group, and it writes **no** `tabindex`
|
|
2816
|
+
— the tab stop lives in the focus controller, not in the markup.
|
|
2817
|
+
- **The polyfill otherwise.** The low-level `FocusGroup` class of `@microsoft/focusgroup-polyfill` (a
|
|
2818
|
+
dependency, MIT, 5.5 KB gz) over an array-backed item collection — never `polyfill()` and never a
|
|
2819
|
+
`MutationObserver`, so the host's own reconciliation drives `update()`. This provider renders the tab stop as
|
|
2820
|
+
`tabindex="0"` / `"-1"`, saving and restoring the author's values.
|
|
2821
|
+
- **Call `update()`** when the item set, an item's visibility or the definition changes. The adapter observes
|
|
2822
|
+
nothing.
|
|
2823
|
+
- **Teardown, in this order:** `update({ definition: {} })`, which restores the authors' `tabindex`, then
|
|
2824
|
+
`disconnect()`. `disconnect()` alone leaves `tabindex` rewritten (measured in Chromium 153 and 156).
|
|
2825
|
+
- **A consumed key** is a `preventDefault()` on `keydown`: both providers honour it. Register the listener
|
|
2826
|
+
that decides _before_ the adapter's own (the polyfill's is on the owner, so a Solid `onKeyDown`, delegated to
|
|
2827
|
+
the document, is too late — attach it in the owner's `ref`, as `createToolbar` does).
|
|
2828
|
+
- **`focusGroupPolyfill`** is for a test that must prove the fallback in an engine that has the native feature,
|
|
2829
|
+
and for a host that knows it needs the polyfill. It removes the owner's `focusgroup` attribute so the native
|
|
2830
|
+
engine stays out of the group, and hides the `focusGroup` IDL property for the one constructor call the
|
|
2831
|
+
polyfill's own feature detect guards.
|
|
2832
|
+
|
|
2833
|
+
### Headless: `focusGrid` (`@cssints/headless`)
|
|
2834
|
+
|
|
2835
|
+
The platform layer of `focusgroup="grid"` (V2): two-dimensional directional navigation over a rectangular, spanless
|
|
2836
|
+
body, one tab stop, entry / memory — the behaviour `packages/apg-specs/specs/patterns/focusgrid.qnt` models. It is
|
|
2837
|
+
**ours** because V2 is unpublished: no engine ships `grid`, and `@microsoft/focusgroup-polyfill` 1.6.0 (npm's latest)
|
|
2838
|
+
has no grid token at all, while its `main` branch (merged 2026-09-29) has no built `dist`. One definition renders the
|
|
2839
|
+
attribute the platform will read and drives our movement.
|
|
2840
|
+
|
|
2841
|
+
```ts skip
|
|
2842
|
+
focusGrid(owner: HTMLElement, options: {
|
|
2843
|
+
definition: {
|
|
2844
|
+
manual?: boolean; // "grid manual": the rows are focusgrouprow children, not a native table
|
|
2845
|
+
rowEdge?: "none" | "wrap" | "flow"; // the INLINE edges: rowwrap / rowflow
|
|
2846
|
+
colEdge?: "none" | "wrap" | "flow"; // the BLOCK edges: colwrap / colflow
|
|
2847
|
+
memory?: boolean; // false emits `nomemory`
|
|
2848
|
+
};
|
|
2849
|
+
items: () => ReadonlyArray<{
|
|
2850
|
+
element: HTMLElement; // the cell target of the coordinate
|
|
2851
|
+
row: number;
|
|
2852
|
+
col: number;
|
|
2853
|
+
cell?: HTMLElement; // when `element` is a descendant of its cell
|
|
2854
|
+
}>;
|
|
2855
|
+
provider?: "auto" | "polyfill"; // "polyfill" forces this runtime and writes no focusgroup attribute
|
|
2856
|
+
}): {
|
|
2857
|
+
attribute: string; // the definition as the focusgroup attribute
|
|
2858
|
+
update(definition?: definition): void;
|
|
2859
|
+
destroy(): void; // idempotent
|
|
2860
|
+
};
|
|
2861
|
+
|
|
2862
|
+
focusgridAttribute(definition); // "grid manual rowflow colwrap nomemory"
|
|
2863
|
+
supportsFocusGrid(manual?); // focusGroup.supports("grid") (and "manual")
|
|
2864
|
+
```
|
|
2865
|
+
|
|
2866
|
+
- **What it owns.** The `focusgroup` attribute, exactly one `tabindex="0"` cell (the authors' values are saved and
|
|
2867
|
+
restored by `destroy()`), arrows on both axes, `Home` / `End` for the row's ends, `Ctrl+Home` / `Ctrl+End` for the
|
|
2868
|
+
grid's, the per-axis wrap / flow edges, `focusgroupstart` as the entry target, the last-focused memory and
|
|
2869
|
+
`nomemory`, and the `focusgrouprow` markers of a `manual` topology. A hard edge does not move focus and does not
|
|
2870
|
+
cancel the key.
|
|
2871
|
+
- **The edges are resolved, not tokenised.** `rowEdge` / `colEdge` are the per-axis result, so the attribute's broad
|
|
2872
|
+
`wrap` / `flow` (both axes) and its conflicts (`wrap flow`, `nowrap` with either) are the caller's parse; the
|
|
2873
|
+
attribute we emit is canonical — `rowwrap`, never `wrap` — and a hard axis emits nothing. A grid's default is hard
|
|
2874
|
+
(the explainer: wrapping "can disorient users in data grids"), which is the opposite of the V1 behaviours.
|
|
2875
|
+
- **`manual` is topology only.** It renders `grid manual` and marks each row with `focusgrouprow`; it changes no
|
|
2876
|
+
coordinate, no key and no tab stop. The host owns row discovery either way: `items` gives the coordinates, and a
|
|
2877
|
+
body that is not a rectangle (a ragged row, a span, a duplicate coordinate) decorates nothing and moves nothing —
|
|
2878
|
+
the explainer's "invalid grids do not run".
|
|
2879
|
+
- **A native control keeps its keys.** A cell target that is an `<input>` (not a checkbox or radio), `<textarea>`,
|
|
2880
|
+
`<select>`, `contenteditable`, `<iframe>` / `<object>`, or a `<video>` / `<audio>` with controls does not move the
|
|
2881
|
+
grid: its own behaviour claims the arrow, Home and End.
|
|
2882
|
+
- **Call `update()`** when the body, a `focusgroupstart` marker or the definition changes. Like `focusGroup`, the
|
|
2883
|
+
adapter observes nothing; a host that must consume a key first registers its own listener **before** calling
|
|
2884
|
+
`focusGrid` (a delegated Solid `onKeyDown` runs at the document, after this one).
|
|
2885
|
+
- **Native first, the same definition.** Where `focusGroup.supports("grid")` (and `"manual"`, for a manual topology)
|
|
2886
|
+
the runtime writes only the attribute and its row markers and the browser owns the movement and the tab stop — so
|
|
2887
|
+
the host authors `tabindex` on each cell target, as the explainer's markup does. The polyfill's compatibility trap
|
|
2888
|
+
(it polyfills `grid` even where a browser ships it) does not apply: this is not the polyfill.
|
|
2889
|
+
`provider: "polyfill"` skips the feature detect, drives this runtime and writes no `focusgroup` attribute, so the
|
|
2890
|
+
two engines cannot both act (a test, or a host that knows it needs the fallback).
|
|
2891
|
+
- **What it does not do** (the widget's, `specs/apg/grid.qnt` and `createGrid`): edit mode, selection, sort,
|
|
2892
|
+
activation, `PageUp` / `PageDown`, spans, ragged rows, virtualisation, `itemcontrols` (default for a grid,
|
|
2893
|
+
unsupported upstream), `aria-rowindex` / `aria-colindex` / role inference, and Tab / Shift+Tab (HTML sequential
|
|
2894
|
+
navigation, observed here only as focusout).
|
|
2895
|
+
|
|
2647
2896
|
### Headless: `createToolbar` (`@cssints/headless`)
|
|
2648
2897
|
|
|
2649
|
-
The APG Toolbar: a row or column of controls behind one tab stop, with the
|
|
2650
|
-
|
|
2651
|
-
|
|
2898
|
+
The APG Toolbar: a row or column of controls behind one tab stop, with the platform's `focusGroup` and the
|
|
2899
|
+
delta of `packages/apg-specs/specs/apg/toolbar.qnt` (`aria-orientation`, the author's wrap choice,
|
|
2900
|
+
disabled-but-focusable items, embedded controls that consume keys, Tab out of a text field, and activation).
|
|
2652
2901
|
`packages/headless/test/toolbar.browser.test.tsx` replays the ported ariakit, base-ui and React Aria
|
|
2653
2902
|
scenarios and walks the model horizontally (no wrap) and vertically (wrapping); the fixture composes
|
|
2654
|
-
`createMenu` for the spec's menu-button, which the primitive never imports.
|
|
2903
|
+
`createMenu` for the spec's menu-button, which the primitive never imports. The walk runs on the polyfill
|
|
2904
|
+
provider: native focusgroup ignores an untrusted `keydown` and writes no `tabindex`, and both are what the
|
|
2905
|
+
walk's programmatic steps and `tabindex` probe need (the adapter test covers the native path).
|
|
2655
2906
|
|
|
2656
|
-
|
|
2657
|
-
|
|
2907
|
+
**Migration, if you rendered the item props before:** drop `tabindex` from your item markup and let the
|
|
2908
|
+
provider write it. `ToolbarItemProps` no longer carries `tabindex`, and the owner must now take
|
|
2909
|
+
`toolbarProps` (which carries `focusgroup`, `role`, `aria-orientation` and the `ref` that builds the
|
|
2910
|
+
provider). Nothing else about the item props changes; `toolbarProps` gained `focusgroup` and `ref` and no
|
|
2911
|
+
longer carries `onKeyDown`, because the `ref` registers that listener before the provider's.
|
|
2658
2912
|
|
|
2659
2913
|
`items` is fixed at construction, as label strings or `{ label, kind, disabled }`, and `kinds` names a kind per
|
|
2660
2914
|
index for the string form (`"button"` by default). `defaultFocus` is the index that starts focused, and
|
|
2661
|
-
`focus: -1` means "outside the toolbar".
|
|
2915
|
+
`focus: -1` means "outside the toolbar". `provider: "polyfill"` forces the fallback (tests, hosts without the
|
|
2916
|
+
native attribute).
|
|
2662
2917
|
|
|
2663
2918
|
```ts skip
|
|
2664
2919
|
createToolbar(options: {
|
|
@@ -2670,6 +2925,7 @@ createToolbar(options: {
|
|
|
2670
2925
|
disabled?: ReadonlyArray<number>; // items disabled at mount, still focusable
|
|
2671
2926
|
id?: string; // base of `<id>-item-<i>`
|
|
2672
2927
|
onActivate?(index: number): void; // a button item, by click, Enter or Space
|
|
2928
|
+
provider?: "auto" | "polyfill"; // "auto": the native attribute where the engine has it
|
|
2673
2929
|
}): {
|
|
2674
2930
|
focus(): number; // -1 when the toolbar is left
|
|
2675
2931
|
tabStop(): number;
|
|
@@ -2679,35 +2935,40 @@ createToolbar(options: {
|
|
|
2679
2935
|
focusIn(): void; // Tab into the toolbar: the tab stop
|
|
2680
2936
|
leave(): void;
|
|
2681
2937
|
focusItem(index: number): void;
|
|
2682
|
-
keyDown(key: string): void;
|
|
2683
2938
|
tab(back: boolean): void;
|
|
2684
2939
|
clickItem(index: number): void;
|
|
2685
2940
|
activate(): void; // a button item
|
|
2686
2941
|
toggleDisabled(index: number): void;
|
|
2687
|
-
setOrientation(orientation: "horizontal" | "vertical"): void;
|
|
2688
|
-
setWrap(wrap: boolean): void;
|
|
2942
|
+
setOrientation(orientation: "horizontal" | "vertical"): void; // update()s the provider
|
|
2943
|
+
setWrap(wrap: boolean): void; // update()s the provider
|
|
2689
2944
|
toolbarProps: {
|
|
2690
2945
|
role: "toolbar";
|
|
2691
2946
|
"aria-orientation": "horizontal" | "vertical";
|
|
2692
|
-
|
|
2947
|
+
focusgroup: string; // "toolbar", "toolbar block", ... — the definition as the attribute
|
|
2948
|
+
ref: (element: HTMLElement) => void; // builds the provider, registers the key handler first
|
|
2693
2949
|
};
|
|
2694
2950
|
itemProps(index: number): {
|
|
2695
2951
|
id;
|
|
2696
2952
|
role: "button";
|
|
2697
2953
|
"aria-disabled": "true" | undefined;
|
|
2698
|
-
|
|
2954
|
+
ref: (element: HTMLElement) => void; // hands the item to the provider
|
|
2699
2955
|
onClick;
|
|
2700
|
-
onFocus;
|
|
2956
|
+
onFocus; // records the index: `focus` / `tabStop` follow the DOM focus
|
|
2701
2957
|
};
|
|
2702
2958
|
};
|
|
2703
2959
|
```
|
|
2704
2960
|
|
|
2961
|
+
- **The provider owns the tab stop.** There is no `tabindex` in `itemProps`, and there is no `keyDown`
|
|
2962
|
+
method: the arrows, Home / End and the wrap are the platform's, and `focus` / `tabStop` follow the DOM
|
|
2963
|
+
`focusin`. A host that needs to move focus calls `focusItem(index)` and focuses the element the props name.
|
|
2705
2964
|
- **Disabled but focusable.** A disabled item keeps its place in the arrows and the tab-stop order and
|
|
2706
2965
|
announces `aria-disabled="true"`; it is never activated, so `onActivate` does not fire for it.
|
|
2707
|
-
`toggleDisabled(index)` flips the mask at runtime.
|
|
2966
|
+
`toggleDisabled(index)` flips the mask at runtime. It is `aria-disabled`, not the native attribute, because
|
|
2967
|
+
the platform skips a natively disabled item.
|
|
2708
2968
|
- **Embedded controls consume their keys.** A `"text"` item swallows the axis navigation keys and a
|
|
2709
|
-
`"spin"` item swallows Up / Down and Home / End
|
|
2710
|
-
|
|
2969
|
+
`"spin"` item swallows Up / Down and Home / End (`preventDefault` on the owner's keydown, before the
|
|
2970
|
+
provider's listener), so typing or stepping inside an item never moves the toolbar's focus. The embedded
|
|
2971
|
+
control's own value is not modelled: the host renders it and owns it.
|
|
2711
2972
|
- **Divergences.** base-ui and ariakit skip disabled items in roving navigation, where `toolbar.qnt` keeps
|
|
2712
2973
|
them focusable; the tab stop starts on item `0` even when that item is disabled, where the abstract
|
|
2713
2974
|
Composite picks the first non-disabled item; and the primitive renders `role="toolbar"` with
|
|
@@ -2715,13 +2976,13 @@ createToolbar(options: {
|
|
|
2715
2976
|
not this primitive.
|
|
2716
2977
|
- **Limits (the source's own):** no separators, no RTL, and `items` does not grow after construction.
|
|
2717
2978
|
|
|
2718
|
-
`Right` / `Left` on a horizontal toolbar and `Down` / `Up` on a vertical one move
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
composite. `focusIn`, `leave` and `tab(back)` are those
|
|
2724
|
-
`activate` are the pointer and programmatic paths.
|
|
2979
|
+
`Right` / `Left` on a horizontal toolbar and `Down` / `Up` on a vertical one move focus along the axis
|
|
2980
|
+
(`ArrowRight` and `Right` are normalised to one key), Home / End jump to the ends, and `wrap` decides whether
|
|
2981
|
+
an edge wraps or stops — all of it from the definition the owner's `focusgroup` attribute states. Enter and
|
|
2982
|
+
Space activate the focused `button` item, and both are `preventDefault`ed. Tab from a `button` item leaves the
|
|
2983
|
+
toolbar (the browser moves on); from a `text` item it steps to the neighbouring item and only leaves at the
|
|
2984
|
+
edge, which is how Tab escapes a text field inside a composite. `focusIn`, `leave` and `tab(back)` are those
|
|
2985
|
+
steps as methods, and `focusItem`, `clickItem` and `activate` are the pointer and programmatic paths.
|
|
2725
2986
|
|
|
2726
2987
|
### Headless: `createTextInput` and `createTextArea` (`@cssints/headless`)
|
|
2727
2988
|
|
|
@@ -2870,6 +3131,18 @@ and walks it differentially in thirteen configurations: single, follows-focus, a
|
|
|
2870
3131
|
multi with and without one, type-ahead with `selectionMode: "none"`, and each of those controlled by
|
|
2871
3132
|
a host that accepts every request and one that holds its value.
|
|
2872
3133
|
|
|
3134
|
+
The roving tab stop is the platform's `focusgroup="listbox"` contract (`shared/focusgroup.qnt`, the
|
|
3135
|
+
`focusGroup` adapter of the toolbar section): arrows along the block axis, no wrap, Home / End, the
|
|
3136
|
+
last-focused memory and the one tab stop are the browser's (Chromium 150+) or
|
|
3137
|
+
`@microsoft/focusgroup-polyfill`'s. The primitive renders the owner's `focusgroup` attribute and its
|
|
3138
|
+
`ref`, keeps every enabled option focusable (`tabindex="0"`, which the polyfill decorates into the
|
|
3139
|
+
stop and restores), and never decides which option is the stop. `focus` / `active` are readings of
|
|
3140
|
+
the DOM: an option's `focus` is the move, so the platform's arrow, a click and a host's own
|
|
3141
|
+
`element.focus()` land in the same state. The library keeps its delta: the selection model,
|
|
3142
|
+
`aria-selected`, range selection, type-ahead, and the option the arrows must not reach (it is out of
|
|
3143
|
+
the item collection and wears `focusgroup="none"`, because the platform reads only `aria-disabled`
|
|
3144
|
+
and would still move focus onto it).
|
|
3145
|
+
|
|
2873
3146
|
```ts skip
|
|
2874
3147
|
// ListboxItemInput is a label string or { id?, label, textValue?, disabled? }
|
|
2875
3148
|
createListbox(options: {
|
|
@@ -2886,6 +3159,7 @@ createListbox(options: {
|
|
|
2886
3159
|
label?: string | undefined; // aria-label on the listbox
|
|
2887
3160
|
onValueChange?(selected: ReadonlyArray<number>): void; // a request; onSelectionChange is an alias
|
|
2888
3161
|
onActiveChange?(index: number): void; // a request
|
|
3162
|
+
provider?: "auto" | "polyfill"; // "auto": the feature detect; "polyfill" forces the low-level group
|
|
2889
3163
|
}): {
|
|
2890
3164
|
active(): number;
|
|
2891
3165
|
selected(): ReadonlyArray<number>; // sorted, de-duplicated, in range
|
|
@@ -2894,7 +3168,7 @@ createListbox(options: {
|
|
|
2894
3168
|
selectionMode: "single" | "multiple" | "none";
|
|
2895
3169
|
focus(): number; // the option that takes focus on entry
|
|
2896
3170
|
blur(): void;
|
|
2897
|
-
keyDown(key: string, modifiers?: { shift?; ctrl?; meta?; alt? }): void;
|
|
3171
|
+
keyDown(key: string, modifiers?: { shift?; ctrl?; meta?; alt? }): void; // the library's keys, not the movement
|
|
2898
3172
|
type(text: string): void;
|
|
2899
3173
|
clearTypeahead(): void;
|
|
2900
3174
|
clickOption(index: number): void;
|
|
@@ -2905,7 +3179,8 @@ createListbox(options: {
|
|
|
2905
3179
|
"aria-multiselectable": "true" | "false" | undefined; // undefined in "none"
|
|
2906
3180
|
"aria-activedescendant": string | undefined; // only while the widget holds focus
|
|
2907
3181
|
"aria-label": string | undefined;
|
|
2908
|
-
|
|
3182
|
+
focusgroup: string; // "listbox block nowrap"; "" when the polyfill is forced
|
|
3183
|
+
ref; // required: it builds the provider and the primitive's keydown listener
|
|
2909
3184
|
onFocusOut;
|
|
2910
3185
|
};
|
|
2911
3186
|
optionProps(index: number): {
|
|
@@ -2915,7 +3190,9 @@ createListbox(options: {
|
|
|
2915
3190
|
"aria-disabled": "true" | undefined;
|
|
2916
3191
|
"aria-posinset";
|
|
2917
3192
|
"aria-setsize";
|
|
2918
|
-
|
|
3193
|
+
focusgroup: "none" | undefined; // "none" on an option the arrows must not reach
|
|
3194
|
+
tabindex: 0 | -1; // the constant focusability value: -1 only when disabled
|
|
3195
|
+
ref;
|
|
2919
3196
|
onClick;
|
|
2920
3197
|
onFocus;
|
|
2921
3198
|
onMouseEnter;
|
|
@@ -2930,35 +3207,44 @@ not walk the whole list. A selection or a `defaultActive` chosen before the item
|
|
|
2930
3207
|
applies once they do, because `selected()` normalizes against the live count at every read.
|
|
2931
3208
|
Selection is a set of indices, always returned sorted and de-duplicated, and an option that is
|
|
2932
3209
|
disabled either by the `disabled` index list or by its own `disabled` field is skipped by the
|
|
2933
|
-
arrows, Home/End, clicks and hover
|
|
3210
|
+
arrows, Home/End, clicks and hover: it is left out of the group's item collection and marked
|
|
3211
|
+
`focusgroup="none"`, so neither provider lands focus on it.
|
|
2934
3212
|
|
|
2935
3213
|
Selection is uncontrolled by default (`defaultValue`, or `defaultActive` for the active option) and
|
|
2936
3214
|
controlled by `value` + `onValueChange` (the active option by `activeValue` + `onActiveChange`,
|
|
2937
3215
|
which is the type-ahead machine's value). Every selection change and every focus move is a request:
|
|
2938
3216
|
uncontrolled it commits, controlled it commits nothing until the host writes the prop back, and a
|
|
2939
|
-
request equal to the current state is not made at all.
|
|
3217
|
+
request equal to the current state is not made at all. A controlled active option that refuses a
|
|
3218
|
+
move also gets the DOM focus put back on the option the state names, because the platform has
|
|
3219
|
+
already moved it.
|
|
2940
3220
|
|
|
2941
3221
|
**Keyboard.** In `"single"` and `"none"` a printable character is type-ahead: a fresh buffer
|
|
2942
3222
|
searches from the option after the active one, wrapping, and a growing buffer searches from the
|
|
2943
3223
|
active one inclusive, both on the item's `textValue` (its label by default); a Space continues a
|
|
2944
3224
|
non-empty buffer instead of selecting. ArrowDown / ArrowUp move the active option without wrapping,
|
|
2945
|
-
Home / End jump to the first / last enabled option
|
|
2946
|
-
single-select (in `"none"` there is nothing
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
3225
|
+
and Home / End jump to the first / last enabled option — the platform's movement, not the
|
|
3226
|
+
primitive's. Space or Enter selects the active option in single-select (in `"none"` there is nothing
|
|
3227
|
+
to select). In `"multiple"` Space toggles the active option, Shift+Space selects the run from the
|
|
3228
|
+
anchor to the active option, Shift+Arrow toggles the option it enters (the platform ignores the
|
|
3229
|
+
modifier, so the primitive moves the active option and the host's focus mirror follows), Ctrl+Shift+
|
|
3230
|
+
Home / End select to that edge, and Ctrl+A (or Meta+A) selects all, or clears when all are selected.
|
|
3231
|
+
Ctrl+Space and Ctrl+Arrow are consumed and move nothing: the pattern's alternative model, where the
|
|
3232
|
+
arrows move focus only, is not implemented. The type-ahead keys are single- and none-only;
|
|
3233
|
+
`type(text)` itself searches in any mode, and `keyDown(key)` drives exactly these library keys — an
|
|
3234
|
+
arrow or Home / End passed to it moves nothing, because the platform is the mover.
|
|
2952
3235
|
|
|
2953
3236
|
There is no live region and no timer: the host calls `clearTypeahead()` from its own idle timer, and
|
|
2954
3237
|
`focusOnHover` is off by default because moving the active option on hover fights a nested scroll or
|
|
2955
3238
|
a tooltip. `focus()` names the entry option (the first selected one, else the first), returns the
|
|
2956
3239
|
active one when the widget already has focus, and, in single-select with `followsFocus`, commits it.
|
|
2957
|
-
`aria-activedescendant` is emitted while the widget holds focus
|
|
2958
|
-
tabindex: the
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
the
|
|
3240
|
+
`aria-activedescendant` is emitted while the widget holds focus and the container itself has no
|
|
3241
|
+
tabindex: the rows are the focus holders, and the host moves the DOM focus onto the active row for a
|
|
3242
|
+
programmatic move (type-ahead, a click, a controlled `activeValue`); the platform's own moves need no
|
|
3243
|
+
mirror. A host that instead keeps DOM focus on the container — a combobox popup, or
|
|
3244
|
+
`createRearrangeableListbox` — is in the `aria-activedescendant` model, which is not a focusgroup and
|
|
3245
|
+
keeps its own arrow handling. The scenario divergences: `autoFocus` focuses the option, not the
|
|
3246
|
+
listbox, so a probe reads `option:0`; and `onAction` / `onPress` are react-aria extensions the
|
|
3247
|
+
primitive does not report (only the selection change).
|
|
2962
3248
|
|
|
2963
3249
|
### Headless: `createMeter` (`@cssints/headless`)
|
|
2964
3250
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cssints",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "CSS in TypeScript: typed css.* calls compiled to atomic CSS at build time (a Vite plugin)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -72,12 +72,12 @@
|
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@cssints/css-grammar": "^0.
|
|
75
|
+
"@cssints/css-grammar": "^0.34.0",
|
|
76
76
|
"culori": "^4.0.2",
|
|
77
77
|
"effect": "4.0.6",
|
|
78
|
-
"happy-dom": "^20.14.
|
|
79
|
-
"lightningcss": "^1.
|
|
80
|
-
"magic-string": "^0.30.
|
|
78
|
+
"happy-dom": "^20.14.6",
|
|
79
|
+
"lightningcss": "^1.33.0",
|
|
80
|
+
"magic-string": "^0.30.21",
|
|
81
81
|
"mdn-data": "2.27.1",
|
|
82
82
|
"oxc-parser": "^0.149.0"
|
|
83
83
|
},
|