cssints 0.13.1 → 0.14.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.
Files changed (2) hide show
  1. package/docs/API.md +43 -0
  2. package/package.json +2 -2
package/docs/API.md CHANGED
@@ -1291,6 +1291,49 @@ createConfirm(options?: { id?: string | undefined; onOpenChange?(open: boolean):
1291
1291
 
1292
1292
  `confirm()` resolves `true` on confirm and `false` on cancel, Escape and a backdrop click. A second request while one is pending rejects with `ConfirmPendingError` (it is not queued). Focus goes to the cancel button on open and back to the element that had it when `confirm()` was called. While it is open the dialog has a shell: Tab and Shift+Tab cycle inside it, `overflow: hidden` is set on `document.documentElement` (restored on close; counted, so nested confirms restore it once), and Escape is read from the document in the capture phase, so it works with the focus outside the dialog. Confirms from different `createConfirm` calls may be open together: the last opened gets Escape and Tab. The default labels stay English; a translating host passes `confirmLabel` and `cancelLabel` with each request.
1293
1293
 
1294
+ ### Headless: `createPopover` (`@cssints/headless`)
1295
+
1296
+ **No APG pattern exists** for a popover (APG has Dialog, which is modal), so it has no Quint spec and no ported scenarios: `packages/headless/test/popover.browser.test.tsx` is its oracle. The behaviour is copied from Base UI's Popover (a trigger click toggles, Enter / Space / ArrowDown open, an outside `pointerdown` light-dismisses without stealing focus) and React Aria's `usePopover` (focus into the content on open and back to the trigger on close, `role="dialog"` without `aria-modal`, a placement that flips at the viewport edge).
1297
+
1298
+ ```ts skip
1299
+ createPopover(options?: {
1300
+ id?: string | undefined;
1301
+ open?: boolean | undefined; // controlled: the host owns the state
1302
+ defaultOpen?: boolean | undefined;
1303
+ placement?: "bottom-start" | "bottom" | "bottom-end" | "top-start" | "top" | "top-end" | undefined; // "bottom"
1304
+ offset?: number | undefined; // the gap to the trigger, 4
1305
+ modal?: false; // a popover is never modal: inert, the scroll lock and the Tab trap are createDialogModal's
1306
+ onOpenChange?(open: boolean): void;
1307
+ }): {
1308
+ open(): boolean;
1309
+ triggerProps: {
1310
+ id;
1311
+ "aria-haspopup": "dialog";
1312
+ "aria-expanded": "true" | "false";
1313
+ "aria-controls": string;
1314
+ onClick(): void;
1315
+ onKeyDown(event: KeyboardEvent): void; // Enter / Space / ArrowDown open
1316
+ };
1317
+ contentProps: {
1318
+ id;
1319
+ role: "dialog";
1320
+ "aria-modal": undefined; // always: not modal
1321
+ tabindex: -1;
1322
+ hidden: boolean;
1323
+ onKeyDown(event: KeyboardEvent): void; // Escape closes, and focus goes back to the trigger
1324
+ };
1325
+ contentStyle(): { position: "absolute"; top: string; left: string }; // the last measure
1326
+ position(
1327
+ triggerRect: { top; left; width; height },
1328
+ contentRect: { top; left; width; height },
1329
+ viewport: { width; height },
1330
+ ): { top: number; left: number; placement: PopoverPlacement }; // pure; flips at the viewport edge
1331
+ attach(trigger: HTMLElement, content: HTMLElement): () => void; // call from a ref; returns the teardown
1332
+ };
1333
+ ```
1334
+
1335
+ `attach` is the DOM contract: on open it measures and places the content (and again on resize, scroll and a `ResizeObserver`), focuses the content's first focusable element (else the content, which is `tabindex="-1"`), and on close returns the focus to the trigger unless the close came from an outside `pointerdown` — that one leaves the focus where the page put it. The page behind stays interactive: there is no `inert`, no scroll lock and no Tab cycle, and the content is not a menu (no roving focus: `createMenu` in a popover owns its items). The rects and the viewport passed to `position` must share a space; `attach` measures in client space and writes page space for `position: absolute`.
1336
+
1294
1337
  ### Publishing a library (decided 2026-10-02, cssints-0l3f)
1295
1338
 
1296
1339
  A package written on cssints ships JavaScript that still has the attribute, and its `.d.ts`. The app that installs it evaluates the package with its own engine: one sheet, the app's browserslist and themes, and a token, a kernel or an `@property` of the package is made once however many modules use it. Nothing is declared: the engine recognises a file by `with { type: "cssints" }`, the same marker as in app code.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cssints",
3
- "version": "0.13.1",
3
+ "version": "0.14.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,7 +72,7 @@
72
72
  }
73
73
  },
74
74
  "dependencies": {
75
- "@cssints/css-grammar": "^0.13.1",
75
+ "@cssints/css-grammar": "^0.14.0",
76
76
  "culori": "^4.0.2",
77
77
  "effect": "4.0.6",
78
78
  "happy-dom": "^20.14.5",