diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b05e030..11b9e79e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,15 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: + # S.A0 — proof:published-on-master asserts release-tag → master + # ANCESTRY (`git rev-list -n1 v` + `merge-base --is-ancestor` + # against origin/master); the default shallow depth-1 checkout carries + # neither tags nor history, so that gate exited 128 ("unknown + # revision") on every CI run since its authoring. Full history + tags + # make the ancestry assertion actually runnable on the runner. - uses: actions/checkout@v5 + with: + fetch-depth: 0 # kf runs Node 24 — the newest runtime the published lib will face; # constellation default is 22; engines floor is >=22 (a NAMED delta, # not silent drift — a library should test the newest runtime). @@ -115,11 +123,43 @@ jobs: # surface. HARD posture — a correctness/precept invariant. - name: proof:alias-dropped (Q.WE1 — the @deprecated aliases are GONE from the published d.ts · NO-LEGACY) run: npm run proof:alias-dropped - # Q.WE1 — the breaking-set MIGRATION-doc oracle: docs/MIGRATION-5.0.0.md - # documents the full breaking alias-drop set + the globalThis.ScrollTimeline - # disambiguation note (semver honesty; consumed by Q.WZ's 5.0.0 cut). - - name: proof:changelog-5 (Q.WE1 — the breaking 5.0.0 alias-drop set is documented for the consumer) - run: npm run proof:changelog-5 + # T.M8 (lane 27 F9) — the three S.B6 type-surface / ./engine-drift gates + # folded out of their dev-only orphan status into a real tier. They read + # the BUILT dist/keyframes.d.ts (same precondition as proof:published- + # surface / proof:alias-dropped above — after build:lib), so they ride the + # gates job here + proof:hygiene-chain (local truth). Was: EXCLUDED as + # "development-only" (never CI-wired) since S.B6. + - name: proof:engine-subpath-mirror (T.M8/F9 — ./engine subpath mirrors the AnimationEngine keys) + run: npm run proof:engine-subpath-mirror + - name: proof:no-any-default (T.M8/F9 — no `any` in the default type surface) + run: npm run proof:no-any-default + - name: proof:dts-rollups-agree (T.M8/F9 — the d.ts roll-ups agree) + run: npm run proof:dts-rollups-agree + # T.M — the born-OWNER gate class mechanism (batch ①). Pure static reads + # (docs/tree/package.json), no browser, no dist — so they ride the fast + # gates job + proof:hygiene-chain. Each is green-today (M1/M2 vacuous or + # clean; M6/M7 registry-derived; M9 board-live; M10 recap teeth). The + # BORN-RED backlog gates (proof:stage-inventory / -subject-legible / + # -subject-full / -roster-ceiling) are DECLARED in scripts/gate-bands.mjs + # T_BORNRED_BACKLOG + EXCLUDED in proof:ci-coverage (recorded, not blocking). + - name: proof:owner-verdict-recorded (T.M1 — K's TASTE-VERDICT revived, blocking per-wave) + run: npm run proof:owner-verdict-recorded + - name: proof:owner-review-gate (T.M2 — pre-authoring sign-off + convergence≠sufficient) + run: npm run proof:owner-review-gate + - name: proof:gate-authority (T.M6 — the INSTRUMENT/OWNER authority axis + blocking-not-OBSERVE) + run: npm run proof:gate-authority + - name: proof:retirement-ledger (T.M7 — feature-coupled retirement, derive-from-tree) + run: npm run proof:retirement-ledger + - name: proof:board-live (T.M9 — board↔tree + session-log freshness) + run: npm run proof:board-live + - name: proof:prompt-recap-t (T.M10 — the born-OWNER recap gate teeth) + run: npm run proof:prompt-recap-t + # S.C1 — the GENERALIZED breaking-set MIGRATION-doc oracle (C-18): git- + # reconstructs the previous published tag's docs/published-surface.md, diffs it + # against HEAD, and REDs on any removed public-surface row lacking a matching + # docs/MIGRATION-.md entry (version-agnostic semver honesty). + - name: proof:changelog (S.C1 · C-18 — every removed published-surface row is documented in docs/MIGRATION-.md) + run: npm run proof:changelog # K.W12 ED-1 — the agent index (/llms.txt + /llms-full.txt) can never # drift from the J.W5 published surface (the gate-ORACLE at the AGENT # boundary: linked exports == the manifest roster, cited gates real). @@ -170,8 +210,24 @@ jobs: run: npm run proof:packrat-sound - name: proof:engine (D.W4 transposition source gate — tick-canon · seam · pause-honest · snap-symmetry · no-legacy) run: npm run proof:engine - - name: proof:no-flat-siblings (R.W1 — the 7-zone directory partition · no flat-hyphenated siblings · zone barrels · known-violations dissolved) + - name: proof:compile-backward-leg (S.B3 — compile/ FORWARD/BACKWARD layering · no re-export bridge · no cross-zone deep-import) + run: npm run proof:compile-backward-leg + - name: proof:zone-cohesion (T.F22 — per-zone internal cohesion · 400L intra-zone ceiling or justified single-concern · ring-fence intact · no test in src) + run: npm run proof:zone-cohesion + - name: proof:colocation (T.F21 — THE EDICT keystone · recursive colocation, kind-appropriate tiers, no flat use* beside composables/) + run: npm run proof:colocation + - name: proof:style-file-ceiling (T.F — the styles split ceiling · every @/styles file ≤300L) + run: npm run proof:style-file-ceiling + - name: proof:no-flat-siblings (R.W1 — the 11-zone directory partition · no flat-hyphenated siblings · zone barrels · known-violations dissolved) run: npm run proof:no-flat-siblings + - name: proof:no-orphan-module (S.C1 — dynamic-import-aware reachability walker + the scoped front-door `animate` grep; the zero-importer zombie class made structural) + run: npm run proof:no-orphan-module + - name: proof:no-dead-dependency (S.C3a — the 8 zero-importer shadcn devDeps + SPRING_SMOOTH denylist, the dead-identifier grep; the shadcn census clause is NON-GATING, discharged at S.C3b) + run: npm run proof:no-dead-dependency + - name: proof:no-dead-export (T.F23a — the demo export-census; a named demo export with zero consumer across demo/src/test/scripts, the granularity no-orphan-module (file) + no-dead-dependency (package) miss; ratchets the deferred backlog DOWN) + run: npm run proof:no-dead-export + - name: proof:any-ceiling (T.F23b — the demo `any`-count ratchet; the count may only fall, a fresh `any` REDs) + run: npm run proof:any-ceiling - name: proof:zero-alloc (AnimationGroup composite allocates 0 bytes/frame + FrameCompiler seam + snap symmetry) run: npm run proof:zero-alloc # F.W2 — the library-scoped engine/correctness gates the workflow @@ -191,8 +247,6 @@ jobs: run: npm run proof:replay-equality - name: proof:orchestration (F.W3 — stagger/FLIP/decay/Sequence behaviour locks) run: npm run proof:orchestration - - name: proof:animate-orchestration (L.W8 S3 — animate() dispatches the orchestration tier; AnimationGroup/Sequence → .play(), W127) - run: npm run proof:animate-orchestration - name: proof:transport-events (L.W5 S3 — Sequence segment/label crossing callback channel) run: npm run proof:transport-events - name: proof:interp-fastprops (F.W4 — the dict-mode buffer fold + single-frame alias) @@ -205,8 +259,6 @@ jobs: run: npm run proof:roundtrip-easing - name: proof:adapter-capture (F.W8 — composition capture + shorthand-applied + AST-decide) run: npm run proof:adapter-capture - - name: proof:motion-path (F.W12 — CSS-native MotionPath eligibility + compositor) - run: npm run proof:motion-path - name: proof:blend (G.W17 — the dead add/weighted blend leaf, element-wise) run: npm run proof:blend - name: proof:composition-honored (K.W7 — the engine HONORS the animation-composition operator add/accumulate/composite) @@ -231,8 +283,6 @@ jobs: run: npm run proof:ingest-replay - name: proof:orbital-rotate3d (G.W18 — the orbital rotate3d output collapse) run: npm run proof:orbital-rotate3d - - name: proof:asset-store-singleton (G.W8 — useAssetManager createGlobalState identity) - run: npm run proof:asset-store-singleton - name: proof:scene-raf-leak (G.W9 — the scene rAF-leak lifecycle correction) run: npm run proof:scene-raf-leak - name: proof:scene-contract-identity (H.W1 — the raw-rAF ScenePlayback round-trip + easing↔cube cross-pairs) @@ -280,32 +330,44 @@ jobs: # no unproven code). A vitest-bench probe over src/ — library job. - name: proof:spring-vector (L.W7 S2 — the SpringProgress vector-sugar ADOPT-or-KILL probe · MEASURE-FIRST, no unproven code) run: npm run proof:spring-vector - # H.W8 S1 (I-1) — the SCENES manifest is RE-SOURCED from demo/app/scenes.ts: + # H.W8 S1 (I-1) — the SCENES manifest is RE-SOURCED from demo/app/scene/scenes.ts: # the runtime SCENES keys ≡ scenes.ts ids (bidirectional set-equality, NOT a # hard count — auto-tracks every scene add/remove). A new scene with no manifest # entry, or a stale manifest key, reds — so the 3 previously-unmanifested scenes # (sequence/motion-path were never gate-visited) are enrolled in every runtime gate. - name: proof:manifest-sourced (H.W8 S1/I-1 — SCENES keys ≡ scenes.ts ids · auto-enrolls every scene) run: npm run proof:manifest-sourced - # I.W7 S6 — proof:gate-is-runtime: THE META-GATE that makes the gate-ORACLE - # precept MECHANICALLY PRIOR (RED-1). For EVERY proof:correctness member — - # the roster is DERIVED from the tier's membership (J.W3 S4/T4), never a - # hardcoded list — it asserts the script opens a browser (serveDist + - # KF_PLAYWRIGHT_DIR + newContext, inline or via the lib lifecycle) AND - # actuates (click/dispatch/mouse/key/drag/navToScene-SWITCH) AND is wired - # into the correctness tier of proof:all. A source-shape / load-rest / - # proxy §Hard gate FAILS. HYGIENE-tier itself (it reads gate SCRIPTS) — it - # polices the GATES' SHAPE; static, no browser/build. - - name: "[HYGIENE-meta] proof:gate-is-runtime (I.W7 S6 — every wave's §Hard gate is an actuating runtime gate wired to the correctness tier · the precept, machine-enforced)" + # I.W7 S6 (RE-TARGETED S.A4) — proof:gate-is-runtime: THE META-GATE that makes + # the gate-ORACLE precept MECHANICALLY PRIOR (RED-1). For EVERY proof:demo- + # correctness member (the S.A4-renamed browser-actuator tier) — the roster is + # DERIVED from the tier's membership (J.W3 S4/T4), never a hardcoded list — it + # asserts the script opens a browser (serveDist + KF_PLAYWRIGHT_DIR + newContext, + # inline or via the lib lifecycle) AND actuates (click/dispatch/mouse/key/drag/ + # navToScene-SWITCH) AND is wired into the demo-correctness tier of proof:all; + # the SYMMETRIC clause asserts every proof:library-correctness member carries NO + # browser harness (mis-tier reds BOTH ways). A source-shape / load-rest / proxy + # §Hard gate FAILS. HYGIENE-tier itself (it reads gate SCRIPTS) — static, no build. + - name: "[HYGIENE-meta] proof:gate-is-runtime (I.W7 S6 · S.A4 — every demo-correctness gate is an actuating runtime gate + every library-correctness gate is harness-free · mis-tier reds both ways)" run: npm run proof:gate-is-runtime - # I.W7 S4 — proof:chronic-closure REWIRED: parse the canonical chronic table - # (docs/tranches/I/PROGRESS.md §"Open deferrals", which SUPERSEDES H's) and - # assert each row exits via a RUNTIME gate that BIT — its script opens a - # browser AND actuates AND was witnessed born-RED — wired to the correctness - # tier. A source-shape / load-rest / proxy gate, a vaporware-targeted HANDOFF, - # or a wrong-axis gate REDS. RETIRED gate names required ABSENT (the dual). It - # polices the chronic ROWS' cited gates; static, HYGIENE-tier, no browser. - - name: "[HYGIENE-meta] proof:chronic-closure (I.W7 S4 — every chronic exits via a RUNTIME gate that BIT · vaporware HANDOFF + wrong-axis gate red · the keystone fixed)" + # I.W7 S4 + S.A1 — proof:chronic-closure: parse the canonical chronic table + # (docs/tranches/S/PROGRESS.md §"Open deferrals", which SUPERSEDES R's, the + # R→S re-point) and assert each kf-runtime-closing row exits via a RUNTIME gate + # that BIT AND the S.A1 SUBSTANCE clause holds — a *-TERMINATED terminal-claim + # must cite a deterministic re-shaped gate (device dependence folded OUT so it + # REDs honestly on any runner; macOS reproduction is the discriminator, the + # Linux-container apparatus DROPPED — SA-7) or a ratified KILL, and no + # disposition may launder a deferral verb (observe/watch/re-affirm/verify) over + # a sacred 6-cell parse shape (C-20/SA-6/T3-x2-#8). Static, HYGIENE-tier, no + # browser. Was BORN-RED BY DESIGN during the S drive (the S ledger is the + # forward disposition board, so the runtime-gate contract discharges as each + # owning wave lands its gate — GREEN only re-run on the merged tree at S.Z2). + # DISCHARGED at T.S1 (the S.Z2 RE-EXECUTION substitute): the residual-row C-20 + # triage re-pointed each landed row's closure at its NOW-LIVE gate (backticked + # + tiered + born-RED witness) OR ratified-KILL for the OD-1-pruned surfaces. + # The gate exits 0 on the merged tree, so it JOINS the blocking gates job here + # (this step re-wired in the same commit its EXCLUDED entry + T_BORNRED_BACKLOG + # row are deleted — drive clause 7) + stays a proof:hygiene-chain member. + - name: proof:chronic-closure (I.W7 S4 · S.A1 → T.S1 — the S chronic ledger closes to RUNTIME + SUBSTANCE discipline; DISCHARGED, now blocking) run: npm run proof:chronic-closure - name: proof:ci-coverage (F.W2 — every proof:* gate is invoked in CI) run: npm run proof:ci-coverage @@ -321,8 +383,25 @@ jobs: # (no browser, glass-ui-free) — reads the demo Vue SFC tree, asserts the # `length > 1` count guard. A future component re-rendering a single-option # dropdown unconditionally reds. Rides the library gates job (device-independent). - - name: proof:no-single-option-select (L.W4 S5 — the U4 lone-option count-guard is live in every scene-driven select) + - name: proof:no-single-option-select (L.W4 S5 / RE-CHARTERED T.C1 — single/zero option ⇒ NOTHING, the #17 dup is elided) run: npm run proof:no-single-option-select + # T.C1/T.C7 — the three STRUCTURAL dock oracles (the meta-fact cure: the + # S roster never looked at a dock's grammar). STATIC source gates (no + # browser, glass-ui-free) — the dock GRAMMAR (glass-ui DockSeparator, + # no hand-rolled dividers, identity leads, serif flip deleted, home = + # compass), the single-option ELISION (single/zero ⇒ NOTHING), and the + # single TOOLTIP authority (no native `title=`). Ride the library gates job. + - name: proof:dock-grammar (T.C1 — both docks speak glass-ui DockSeparator grammar; no hand-rolled dividers; home is compass only) + run: npm run proof:dock-grammar + - name: proof:dock-elision (T.B5-RENDER — single/zero option ⇒ NOTHING; the #17 Spring│Spring dup is elided) + run: npm run proof:dock-elision + - name: proof:dock-single-tooltip (T.C3 — ONE tooltip authority; no native `title=` double-render) + run: npm run proof:dock-single-tooltip + # T.B10/T.C1 — proof:transport-play-first-render: DISCHARGED (was born-RED). + # The rail-core rebuild renders play FIRST (from actions.primary); this joins + # the blocking gates job in the same commit as the render (drive clause 7). + - name: proof:transport-play-first-render (T.C1 — play drawn first from actions.primary; VERDICT #6) + run: npm run proof:transport-play-first-render # L.W4 S2 — proof:settle-is-predicate: the load-bearing openControlsPanel # helper settles on a STATE PREDICATE (waitForRender), never a fixed-ms sleep # (the 259-sleep macOS-pass / Linux-fail render-race class). STATIC grep over @@ -344,6 +423,34 @@ jobs: # demand a consume-edge deletion that the sibling publish has made safe. - name: proof:workaround-deletion (L.W9 — the Band-B consume-edge deletion ledger · PENDING today · RED only when a sibling root-fix publishes) run: npm run proof:workaround-deletion + # T.H1 — proof:glass-ui-gap-tripwire: the generalized glass-ui gap ledger + # (demo/glass-ui-gaps.ts) version tripwire. Reads the installed glass-ui + # version + the SINGLE glassCaps probe (scripts/lib/glass-caps.mjs, reused + # by proof:workaround-deletion); RED when a gap's cure is satisfied in the + # consumed dist while its workaround survives (vacuously green on 4.0.1), + # + the lane-25-rec-2 citation clause (every band-aid site cites its ledger + # entry). STATIC dist-content grep, no browser — the fast gates job. + - name: proof:glass-ui-gap-tripwire (T.H1 — the glass-ui gap ledger version tripwire · vacuously green on 4.0.1 · flips RED on the BG/BH publish while the workaround survives) + run: npm run proof:glass-ui-gap-tripwire + # T.H4 — proof:no-hand-rolled-labeled-select: zero hand-rolled + # label-adjacent- { previewCache[kf.id] = canvas.toDataURL("image/png"); } } catch { - // Capture failed (no animation, 3D not supported, etc.) — ghost preview shown as fallback + // KEEP: capture failed (no animation, 3D not supported, etc.) — ghost preview shown as fallback } finally { previewLoading[kf.id] = false; } diff --git a/demo/@/components/custom/animation-controls/timeline/TimelineCaret.vue b/demo/@/components/custom/instrument/timeline/TimelineCaret.vue similarity index 97% rename from demo/@/components/custom/animation-controls/timeline/TimelineCaret.vue rename to demo/@/components/custom/instrument/timeline/TimelineCaret.vue index 0e523c90..73d2cd48 100644 --- a/demo/@/components/custom/animation-controls/timeline/TimelineCaret.vue +++ b/demo/@/components/custom/instrument/timeline/TimelineCaret.vue @@ -6,7 +6,7 @@
- {{ Math.round(keyframe.percent) }}% + {{ Math.round(keyframe.percent) }}%
-
+
Capturing...
-
+
{{ prop }}: {{ val }}
diff --git a/demo/@/components/custom/animation-controls/timeline/components/TimelineTrack.vue b/demo/@/components/custom/instrument/timeline/components/TimelineTrack.vue similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/components/TimelineTrack.vue rename to demo/@/components/custom/instrument/timeline/components/TimelineTrack.vue diff --git a/demo/@/components/custom/animation-controls/timeline/composables/useTimeline.ts b/demo/@/components/custom/instrument/timeline/composables/useTimeline.ts similarity index 98% rename from demo/@/components/custom/animation-controls/timeline/composables/useTimeline.ts rename to demo/@/components/custom/instrument/timeline/composables/useTimeline.ts index 97a47041..edf63c82 100644 --- a/demo/@/components/custom/animation-controls/timeline/composables/useTimeline.ts +++ b/demo/@/components/custom/instrument/timeline/composables/useTimeline.ts @@ -2,7 +2,7 @@ import { computed, ref } from "vue"; import type { Ref } from "vue"; import { useRefHistory, debounceFilter } from "@vueuse/core"; import type { InputAnimationOptions } from "@mkbabb/keyframes.js"; -import { defaultAnimationOptions } from "../../stores"; +import { defaultAnimationOptions } from "@state"; import { DEFAULT_CAPTURE_PROPERTIES } from "../timelineTypes"; import type { TimelineState } from "../timelineTypes"; import { useTimelineBuild } from "./useTimelineBuild"; diff --git a/demo/@/components/custom/animation-controls/timeline/composables/useTimelineBuild.ts b/demo/@/components/custom/instrument/timeline/composables/useTimelineBuild.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/composables/useTimelineBuild.ts rename to demo/@/components/custom/instrument/timeline/composables/useTimelineBuild.ts diff --git a/demo/@/components/custom/animation-controls/timeline/composables/useTimelineOps.ts b/demo/@/components/custom/instrument/timeline/composables/useTimelineOps.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/composables/useTimelineOps.ts rename to demo/@/components/custom/instrument/timeline/composables/useTimelineOps.ts diff --git a/demo/@/components/custom/animation-controls/timeline/composables/useZoomPan.ts b/demo/@/components/custom/instrument/timeline/composables/useZoomPan.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/composables/useZoomPan.ts rename to demo/@/components/custom/instrument/timeline/composables/useZoomPan.ts diff --git a/demo/@/components/custom/instrument/timeline/index.ts b/demo/@/components/custom/instrument/timeline/index.ts new file mode 100644 index 00000000..9116f4db --- /dev/null +++ b/demo/@/components/custom/instrument/timeline/index.ts @@ -0,0 +1,8 @@ +// The keyframe-timeline instrument barrel (T.F5). The timeline reaches the +// Monaco CSSCodeEditor, so its SFC is re-exported LAZILY via +// `defineAsyncComponent` — the facility umbrella stays free of the heavy chunk. +import { defineAsyncComponent } from "vue"; + +export const KeyframeTimeline = defineAsyncComponent(() => import("./KeyframeTimeline.vue")); + +export type { TimelineKeyframe } from "./timelineTypes"; diff --git a/demo/@/components/custom/animation-controls/timeline/timelineTypes.ts b/demo/@/components/custom/instrument/timeline/timelineTypes.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/timelineTypes.ts rename to demo/@/components/custom/instrument/timeline/timelineTypes.ts diff --git a/demo/@/components/custom/animation-controls/timeline/utils/flattenVars.ts b/demo/@/components/custom/instrument/timeline/utils/flattenVars.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/utils/flattenVars.ts rename to demo/@/components/custom/instrument/timeline/utils/flattenVars.ts diff --git a/demo/@/components/custom/animation-controls/timeline/utils/snapshotCapture.ts b/demo/@/components/custom/instrument/timeline/utils/snapshotCapture.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/utils/snapshotCapture.ts rename to demo/@/components/custom/instrument/timeline/utils/snapshotCapture.ts diff --git a/demo/@/components/custom/animation-controls/timeline/utils/timelineEngine.ts b/demo/@/components/custom/instrument/timeline/utils/timelineEngine.ts similarity index 100% rename from demo/@/components/custom/animation-controls/timeline/utils/timelineEngine.ts rename to demo/@/components/custom/instrument/timeline/utils/timelineEngine.ts diff --git a/demo/@/components/custom/animation-controls/AnimationControlsGroup.vue b/demo/@/components/custom/instrument/transport/AnimationControlsGroup.css similarity index 53% rename from demo/@/components/custom/animation-controls/AnimationControlsGroup.vue rename to demo/@/components/custom/instrument/transport/AnimationControlsGroup.css index 51cde3fc..86ef2758 100644 --- a/demo/@/components/custom/animation-controls/AnimationControlsGroup.vue +++ b/demo/@/components/custom/instrument/transport/AnimationControlsGroup.css @@ -1,261 +1,8 @@ - - - - - diff --git a/demo/@/components/custom/instrument/transport/AnimationControlsGroup.vue b/demo/@/components/custom/instrument/transport/AnimationControlsGroup.vue new file mode 100644 index 00000000..cc874c1b --- /dev/null +++ b/demo/@/components/custom/instrument/transport/AnimationControlsGroup.vue @@ -0,0 +1,334 @@ + + + + + diff --git a/demo/@/components/custom/KfPillTabs.vue b/demo/@/components/custom/instrument/transport/KfPillTabs.vue similarity index 66% rename from demo/@/components/custom/KfPillTabs.vue rename to demo/@/components/custom/instrument/transport/KfPillTabs.vue index 43670f52..629e97f9 100644 --- a/demo/@/components/custom/KfPillTabs.vue +++ b/demo/@/components/custom/instrument/transport/KfPillTabs.vue @@ -14,6 +14,7 @@ role="tablist" :aria-orientation="orientation" :aria-label="ariaLabel" + :data-glassui-gap="gap.id" class="kf-pill-tabs glass-wash" >