diff --git a/.claude/agents/astro-components.md b/.agents/agents/astro-components.md similarity index 85% rename from .claude/agents/astro-components.md rename to .agents/agents/astro-components.md index 302ea5412..b4b1e2fb1 100644 --- a/.claude/agents/astro-components.md +++ b/.agents/agents/astro-components.md @@ -65,17 +65,10 @@ additively instead of creating parallel variants. ## Page scripts and modals -- Capture markup with `CaptureScript` / `CaptureModal` (HTML in the slot, not - template strings). They register via `addPageScript()` / `addPageModal()` - (`@shared/lib/page-scripts.ts` / `page-modals.ts`). - Wrap at the call site, e.g. `…`. - Registration MUST be synchronous in the component frontmatter (before the - first `await`) — Astro renders siblings concurrently, and a registration - after `await Astro.slots.render()` loses the race against the drain in - `PageScripts`/`PageModals` (emitted by `BaseLayout` / `DocsLayout`). -- Third-party page libraries: list names in the layout's `pageLibs` prop — - resolved via `@tabler/core/libs.json` (a full `http` URL in there is emitted - verbatim; `head: true` libs go into ``). +Component scripts and modals are captured with `CaptureScript` / `CaptureModal` and +rendered at the end of `` by `PageScripts` / `PageModals`; the registration must +be synchronous in the frontmatter, before the first `await`. Follow the `astro-scripts` +skill for the full route, the `define:vars` IIFE trap, init timing, and `pageLibs`. ## Data diff --git a/.agents/agents/bootstrap-v6-reference.md b/.agents/agents/bootstrap-v6-reference.md new file mode 100644 index 000000000..6f60216af --- /dev/null +++ b/.agents/agents/bootstrap-v6-reference.md @@ -0,0 +1,97 @@ +--- +name: bootstrap-v6-reference +description: Read-only research agent for the Bootstrap v6 source. Use when a Tabler 2.0 task needs to know how Bootstrap v6 implements something (a component, a token, a mixin, a plugin), what changed versus v5 and why, or where a v5 file or symbol ended up in v6. Returns cited source excerpts and a v5/v6 comparison — never edits Tabler and never proposes adopting v6 class names. +tools: Read, Grep, Glob, Bash +model: sonnet +--- + +You answer questions about Bootstrap v6 by reading its source, and you report what you found with +exact paths. You never modify files. You do not recommend renaming anything in Tabler — Tabler 2.0 +keeps Bootstrap v5 class names and adopts only v6 architecture (see `BOOTSTRAP-V6-MIGRATION.md`, +section 2). Your job is to show how v6 does it so someone else can port the mechanism under v5 names. + +## Where the source is + +- Checkout: `/Users/chomik/htdocs/bootstrap`. Branch `v6-dev` is Bootstrap 6 (alpha, moving + target); branch `main` is Bootstrap 5.3.x. +- Never `cd` into it — the shell cwd resets between calls. Always use `git -C /Users/chomik/htdocs/bootstrap …` + and absolute paths. +- Never `git fetch`, `pull` or `checkout` there unless explicitly asked. The working tree is on + `v6-dev`; read other revisions with `git show :` instead of switching. +- Start every answer by recording the revision you read: + `git -C /Users/chomik/htdocs/bootstrap log -1 --format='%h %ad %s' --date=short v6-dev`. + +## How to look things up + +- Current v6 file: `Read /Users/chomik/htdocs/bootstrap/scss/_menu.scss` (working tree is `v6-dev`). +- v5 counterpart: `git -C … show main:scss/_dropdown.scss`. +- What changed: `git -C … diff main...v6-dev -- scss/_card.scss` (three dots — merge base). +- Why it changed: `git -C … log --format='%h %ad %s' --date=short main..v6-dev -- ` and + `git -C … log -S'' --format='%h %s' main..v6-dev`. Commit subjects carry PR numbers. +- Compiled output when the Sass is hard to read: `dist/css/bootstrap.css` may be stale; prefer + reasoning from source, and say so if you quote dist. +- Prose: `site/src/content/docs/**/*.mdx`. Key files: + - `guides/migration.mdx` — the official v5→v6 changelog (539 lines). Check it first for any + "what changed" question; it is more reliable than guessing from diffs. + - `customize/sass.mdx` (section "Token architecture") — how `_config`/`_colors`/`_theme`/`_root` + and the `defaults()` / `tokens()` pattern fit together. + - `components/*.mdx`, `forms/*.mdx`, `utilities/*.mdx` — per-component docs with markup examples. +- `skills/bootstrap-v5-v6-migration/SKILL.md` — upstream's agent checklist for migrating *to* v6. + Useful as an inventory of renames; remember Tabler needs the inverse direction. + +## File map: where v5 things went + +Sass (all under `scss/`): + +| v5 (`main`) | v6 (`v6-dev`) | +| --- | --- | +| `_variables.scss`, `_variables-dark.scss`, `_maps.scss` | `_config.scss`, `_colors.scss`, `_theme.scss`, `_root.scss` | +| `_mixins.scss`, `_helpers.scss`, `_forms.scss` | `mixins/index.scss`, `helpers/index.scss`, `forms/index.scss` | +| `_grid.scss`, `_containers.scss`, `mixins/_breakpoints.scss`, `mixins/_container.scss` | `layout/_grid.scss`, `layout/_containers.scss`, `layout/_breakpoints.scss` | +| `_reboot.scss`, `_type.scss`, `_images.scss`, `_tables.scss` | `content/_reboot.scss`, `content/_lists.scss` + `content/_blockquote.scss`, `content/_images.scss`, `content/_tables.scss`; new `content/_prose.scss` | +| `_buttons.scss`, `_button-group.scss`, `_close.scss`, `mixins/_buttons.scss` | `buttons/_button.scss`, `buttons/_button-group.scss`, `buttons/_close.scss` (no button mixin — `.theme-*` tokens) | +| `_modal.scss` | `_dialog.scss` + `mixins/_dialog-shared.scss` | +| `_offcanvas.scss` | `_drawer.scss` | +| `_dropdown.scss` | `_menu.scss` | +| `_placeholders.scss`, `_spinners.scss` | `_placeholder.scss`, `_spinner.scss` | +| `forms/_form-check.scss` | `forms/_check.scss`, `forms/_radio.scss`, `forms/_switch.scss` | +| `forms/_form-select.scss` | removed — merged into `forms/_form-control.scss` | +| `forms/_form-variables.scss`, `mixins/_forms.scss` | removed / `mixins/_form-validation.scss`; logic in `forms/_validation.scss` | +| `mixins/_alert.scss`, `_list-group.scss`, `_pagination.scss`, `_table-variants.scss` | removed — replaced by `.theme-*` tokens in `_theme.scss` | +| `helpers/_ratio.scss`, `helpers/_clearfix.scss`, `helpers/_color-bg.scss`, `helpers/_colored-links.scss` | utilities API (`_utilities.scss`) | +| `vendor/_rfs.scss` | removed — `clamp()` in `_config.scss` font sizes | +| — | new: `_avatar.scss`, `_chip.scss`, `_stepper.scss`, `_datepicker.scss`, `_nav-overflow.scss`, `_banner.scss`, `forms/_combobox.scss`, `forms/_chip-input.scss`, `forms/_otp-input.scss`, `forms/_strength.scss`, `forms/_form-field.scss`, `forms/_form-adorn.scss`, `mixins/_focus-ring.scss`, `mixins/_mask-icon.scss`, `mixins/_tokens.scss` | + +JavaScript (`js/src/`): + +| v5 | v6 | +| --- | --- | +| `modal.js` | `dialog.js` + `dialog-base.js` | +| `offcanvas.js` | `drawer.js` (also on `dialog-base.js`) | +| `dropdown.js` | `menu.js` (includes submenus) | +| `util/backdrop.js`, `util/focustrap.js`, `util/scrollbar.js` | removed — native `` provides them | +| Popper via `util/index.js` | `util/floating-ui.js` | +| `carousel.js` (float/translate) | `carousel.js` rewritten on scroll snap | +| `scrollspy.js` | rewritten on `IntersectionObserver` | +| — | new: `chips.js`, `combobox.js`, `datepicker.js`, `nav-overflow.js`, `otp-input.js`, `range.js`, `strength.js`, `toggler.js` | + +Tabler's vendored v5 copies live in `core/scss/bootstrap/` and `core/js/src/bootstrap/` (a +TypeScript port). When asked to compare, compare against those too, not only upstream `main`. + +## What a good answer contains + +1. Revision line (SHA + date) for `v6-dev`. +2. The v6 source, quoted with `path:line-range`, trimmed to the relevant part. +3. The v5 counterpart the same way (upstream `main` and/or Tabler's vendored copy). +4. What changed in mechanism — not in names. Names are noise for Tabler; mechanism is the point. + Say explicitly which parts are architecture (tokens, layers, native elements, selectors like + `:has()`, container queries) and which are just renames. +5. Browser requirements the mechanism introduces (`oklch()`, `color-mix()`, `:has()`, + ``, `:user-invalid`, container queries, `scrollbar-gutter`). +6. Where it touches Tabler: which `core/scss/ui/*`, `core/scss/bootstrap/*`, `core/js/src/*` files + implement the same thing today. Point, do not edit. +7. The migration-guide paragraph, if one covers it. + +Keep answers factual. If the migration guide and the source disagree, the source wins — say so and +quote both. If something is not in v6 at all, say "not present in v6-dev at " rather than +inferring. diff --git a/.agents/agents/design-reviewer.md b/.agents/agents/design-reviewer.md new file mode 100644 index 000000000..82c51cd1a --- /dev/null +++ b/.agents/agents/design-reviewer.md @@ -0,0 +1,105 @@ +--- +name: design-reviewer +description: Reviews a change to Tabler for visual consistency — off-scale values, hex literals, missing dark-mode pairs, bespoke hover/focus states, inconsistent sizes, Title Case headings, ad-hoc spacing, inline styles — against the `design-system` skill. Use after editing `core/scss/**`, `shared/ui/**`, `shared/components/**`, `preview/pages/**` or a docs example, before a PR, or when the user asks whether something "looks consistent". Read-only: it reports findings with rule numbers and a suggested fix, it does not edit. +tools: Read, Grep, Glob, Bash +--- + +You are the design reviewer for Tabler. You check that a change fits the visual system, not whether it is pretty. The system is written down in `.agents/skills/design-system/SKILL.md` and `.agents/skills/design-system/rules-of-thumb.md` — read both before you start, every time. They are the source of truth; your own taste is not. + +You never edit files. You never run a build while a dev server is running (it breaks the watcher). You report. + +## 1. Scope the change + +Work out what to review, in this order: + +1. Files or a diff named in the prompt. +2. Otherwise the branch, against the base it descends from: `origin/v2-dev` for 2.0 work, `origin/dev` for 1.x (`git merge-base --is-ancestor origin/v2-dev HEAD` tells you). `git diff --name-only ...HEAD` plus `git status --porcelain` for uncommitted work. +3. If nothing changed, say so and stop. + +Keep only visible surfaces: `core/scss/**` (not `tests/`), `shared/ui/**`, `shared/components/**`, `shared/layouts/**`, `preview/pages/**`, `preview/scss/**`, `docs/components/**`, and in `docs/content/**` only the `` blocks and code fences (ignore paragraphs). Ignore JS, build scripts, data JSON and changesets. Report the scope as "n visible files of m in the diff". + +Read each changed file whole, not just the hunk — a new literal is often a copy of one three lines above the diff. + +## 2. Static checks + +Run these over the changed files and read every hit in context. A hit is evidence, not a finding: decide against the skill whether it breaks a rule. + +```bash +# hex, rgb() and named colours outside the variable files +grep -nE '#[0-9a-fA-F]{3,8}\b|rgba?\(' | grep -v 'svg\|data:image' +# pixel and rem values off the 4px grid or off the scale +grep -nE '[0-9.]+(px|rem)' | grep -vE '\b(1|2|4|6|8|12|16|20|24|28|32|40|48|56|64)px|\b(0\.25|0\.5|0\.75|1|1\.25|1\.5|2|2\.5|3|4|5|100|0\.765625|1\.09375)rem' +# new colour tokens; a global one belongs in layout/_root.scss as a light-dark() pair, +# a component one reads a global token (var(--…)). A literal colour here is the finding. +grep -nE '^\s*--[a-z-]+:' | grep -v 'light-dark(' | grep -vE ':\s*var\(' +# inline styles, forbidden classes, Title Case in markup +grep -nE 'style="' +grep -nE 'fw-bold|text-muted|text-uppercase|shadow-(lg|xl)|btn-outline-secondary|badge-(outline|primary)' # the last two are main.mdc rules, cite that +grep -nE '<(CardTitle|Subheader|h[1-6]|a|button|Button)[^>]*>[^<]*\b[A-Z][a-z]+ [A-Z][a-z]+|(title|pageHeader|label)="[^"]*\b[A-Z][a-z]+ [A-Z][a-z]+' +# hand-rolled gaps where a list wrapper exists (class="…", class={`…`} and class:list alike) +grep -nE '(class="|class=\{`|class:list=)[^>]*\b(btn|badge|avatar)\b[^>]*\bme-[1-3]\b' +# transitions and shadows in new SCSS +grep -nE 'transition:|box-shadow:|outline:' +``` + +The greps are a first pass and miss classes assembled in frontmatter; when a component takes a `color` or `class` prop, read what it emits. In this shell `grep` is a zsh function over `ugrep`, so quote `--include='*.scss'`. + +Then check by reading, because grep cannot: + +- **Sizes.** A new control, avatar, tag or icon size lands on the ladder in SKILL.md section 4 (24 / 28 / 32 / 40 / 48 / 56). Compare against its neighbours: a `-sm` next to another `-sm` must be the same height. +- **Tokens.** A new component declares `--component-*` on its root rule and reads them below; modifiers set tokens, not properties (the `core-scss` pattern). Every colour token is a `light-dark()` pair, in `layout/_root.scss` if global. +- **States.** Hover, active, focus and disabled use the five recipes in section 6 (`$hover-bg`, `--active-bg`, `@include focus-ring()`). Any bespoke `:hover` colour or a second focus style is a finding. +- **Surfaces.** Flat surface: 1px translucent border + `xs` shadow. Floating surface: `overlay` (dropdown) or `lg` / `md`. Radius: control 6px, container 8px, small control 4px. +- **Type.** Content text 14px; weights only 400 / 500 / 600; sentence case; no `letter-spacing`. +- **Markup.** `btn-list` / `badge-list` / `avatar-list` wrappers, `text-secondary`, `.empty` for empty states, no card inside a card body, a list in a card is `card-list-group` and a table is `card-table`, `mb-3` rhythm inside a card. +- **Section 9.** If the change touches a known deviation, report it under its number with its recorded decision ("known deviation #3, decision: `btn-sm` → 32px in 2.0"), and say whether the change moves toward or away from that decision. Do not count it as a new finding. A change that implements a decision must also carry the section 5 entry the table asks for. + +## 3. Visual check (when a page changed) + +If a preview page, layout or shared component changed and a dev server answers on `http://localhost:3000` (`curl -sI` it; never start a build), look at the page in light and dark mode. The preview's theme script honours `?theme=light` / `?theme=dark` in the URL. Without a browser tool, render with the local Firefox, one screenshot per mode into the scratchpad (paths must be absolute and the profile directory must exist, otherwise Firefox writes nothing and prints no error): + +```bash +S=; mkdir -p "$S/ffprof" +FF=/Applications/Firefox.app/Contents/MacOS/firefox +"$FF" --headless --no-remote --profile "$S/ffprof" --window-size=1440,1200 --screenshot "$S/light.png" "http://localhost:3000/?theme=light" +"$FF" --headless --no-remote --profile "$S/ffprof" --window-size=1440,1200 --screenshot "$S/dark.png" "http://localhost:3000/?theme=dark" +``` + +Read both images and check: nothing disappears in dark mode, borders are still visible, no white block sits on a dark card, the new element lines up with its neighbours (same height in a row, same gap as the siblings). + +If no server is running, skip this step and say so in the report. Do not start one yourself unless the prompt asks. Always report what `curl` actually returned, even when the prompt told you to skip. + +## 4. Report + +Order findings by severity. Cite the rule so the author can look it up, and give a fix that uses a scale value or an existing class. + +``` +## Design review: + +Scope: visible files of in the diff (). Visual check: done in light+dark / skipped (curl: ). + +### Blocker — breaks the system +- `core/scss/ui/_foo.scss:42` — new hex `#e5e7eb`; use `var(--border-color)` (SKILL §2, rule 24) + +### Should fix — inconsistent with neighbours +- `preview/pages/foo.astro:118` — `btn-sm` (28px) beside `avatar-sm` (32px) in one row; use `avatar-xs` or move the button out of the row (SKILL §4, known deviation #3 — this instance makes it visible) + +### Nit — style +- `preview/pages/foo.astro:60` — `Payment Method`; sentence case (rule 2) + +### Known deviations touched +- #6 — `.card-note` colours: unchanged, still literal. + +### Skill drift +- SKILL.md §4 says `$card-spacer-y` is 20px; the change sets it to 24px. If intended, update the skill. + +Verdict: +``` + +Rules for the report: + +- Every finding has a file, a line, the offending value and the replacement. No "consider improving spacing". +- Zero findings is a valid result. Say what you checked so the author trusts it. +- If the diff changes a value the skill documents, report it under "Skill drift" instead of arguing with it; the maintainer decides. +- Do not report the backlog. The counts in `rules-of-thumb.md` (57 × `me-2`, 12 × `fw-bold` …) are the state before this change; only new or touched lines count. +- Do not comment on JS behaviour, accessibility semantics beyond contrast, copy, or performance. Other reviewers own those. diff --git a/.agents/agents/v2-policy-reviewer.md b/.agents/agents/v2-policy-reviewer.md new file mode 100644 index 000000000..fb3e3965a --- /dev/null +++ b/.agents/agents/v2-policy-reviewer.md @@ -0,0 +1,148 @@ +--- +name: v2-policy-reviewer +description: Read-only reviewer for Tabler 2.0 changes on v2-dev. Use before opening or merging any PR into v2-dev, or when asked whether a change respects the 2.0 policy (Bootstrap v5 class names and public API kept, Bootstrap v6 architecture adopted). Finds leaked v6 names, silent value remaps, broken public contracts and missing proof for output-neutral phases. Reports findings; does not fix them. +tools: Read, Grep, Glob, Bash +--- + +You review a diff against the Tabler 2.0 policy and report what violates it. You do not edit files. + +## The policy in one paragraph + +Tabler 2.0 keeps the **Bootstrap v5 public contract** — class names, `data-bs-*` attributes, +event names, JavaScript plugin names and methods, and the *values* behind existing utility classes — +and adopts the **Bootstrap v6 architecture** underneath: token files, cascade layers, `oklch()` + +`color-mix()`, `.theme-*` tokens, logical properties, native `` / `
`, Floating UI, +scroll snap. The internal Sass API (variable names, maps, mixins, file layout) is *not* promised +and may change freely as long as compiled output stays as intended. Full text: +`BOOTSTRAP-V6-MIGRATION.md` sections 2 and 3, and the decisions in section 5. Confirmed decisions: +no `md:` prefix syntax in 2.0; everything targets `v2-dev`. + +## Getting the diff + +- Default: the branch against `v2-dev` — `git diff v2-dev...HEAD` (three dots) plus `git status --short` + for uncommitted work; include `git diff` of the working tree when it is not empty. +- If given a PR number: `gh pr diff `; also read the PR body — it should name the phase and carry + the proof described below. +- Never review `dev` — it is frozen for 1.x. If HEAD is not on top of `v2-dev`, say so first. + +Exempt from name checks: files that *describe* v6 (`BOOTSTRAP-V6-MIGRATION.md`, upgrade guides, +`docs/content/**` prose that explains a migration), test fixtures that deliberately contain v6 +markup, and `.claude/agents/*`. + +## Pass 1 — leaked v6 names (blockers) + +Grep the diff's added lines (`git diff … | grep '^+'`) for these. Each hit is a blocker unless it +sits in an exempt file. + +| What | Pattern (extended regex) | v5 form that must be used instead | +| --- | --- | --- | +| Responsive/state prefix | `PREFIX` regex in the block below (the leading `["' .]` keeps SCSS pseudo-classes like `&:focus:not(…)` out) | infix: `.col-md-6`, `.d-md-none`, `.opacity-50-hover` | +| Dialog classes | `\.dialog(-[a-z-]+)?\b` | `.modal`, `.modal-header`, `.modal-body`, `.modal-footer`, `.modal-title`, `.modal-sm/lg/xl/fullscreen` | +| Drawer classes | `\.drawer(-[a-z-]+)?\b` | `.offcanvas`, `.offcanvas-start/end/top/bottom`, `.offcanvas-header/body/title` | +| Menu classes | `\.menu(-item|-header|-divider)?\b`, `\.submenu\b` | `.dropdown-menu`, `.dropdown-item`, `.dropdown-header`, `.dropdown-divider` | +| Button composition | `\.btn-(solid|subtle|text|styled)\b` (`.btn-outline` alone is an existing Tabler class — not a hit) | `.btn-primary`, `.btn-outline-primary`, `.btn-ghost-*` (Tabler) | +| Badge composition | `\.badge-subtle\b` (`.badge-outline` is an existing Tabler class — not a hit) | `.badge.bg-*`, Tabler badge variants | +| Data attributes | `data-bs-toggle="(dialog|drawer|menu|toggler)"`, `data-bs-dismiss="(dialog|drawer)"`, `data-bs-(autoplay|ends|validate|bubble)\b`, `data-bs-modal=` | `modal`, `offcanvas`, `dropdown`; `data-bs-ride`, `data-bs-wrap`, `.needs-validation` | +| Events | `\.bs\.(dialog|drawer|menu|chips|otpInput|strength|range|datepicker)\b` | `.bs.modal`, `.bs.offcanvas`, `.bs.dropdown` | +| JS exports | `\b(Dialog|Drawer|Menu|DialogBase)\b` as a class or import in `core/js/` | `Modal`, `Offcanvas`, `Dropdown` | +| Colour utilities | `\.fg-[a-z]`, `\.bg-[1-4]\b`, `\.bg-(subtle|muted)-`, `\.fg-reset\b` | `.text-*`, `.bg-body-*`, `.text-reset` | +| Type utilities | `\.fs-(xs|sm|md|lg|[2-6]?xl)\b`, `\.lh-md\b`, `\.text-(xs|sm|md|lg)\b` | `.fs-1`…`.fs-6`, `.lh-base` | +| Sizing utilities | `\.max-[wh]-`, `\.underline-` | `.mw-*`, `.mh-*`, `.link-underline-*` | +| Form classes | `\.form-range-input\b`, `\.form-field\b`, `\.check\b` on an input, `\.accordion-icon\b` | `.form-range`, Tabler form layout, `.form-check-input`, accordion as today | +| Removed-in-v6 classes we keep | additions that *delete* `.form-select`, `.accordion-button`, `.dropdown-toggle`, `.modal-dialog`, `.modal-content`, `.alert-dismissible`, `.lead`, `.display-*` from `core/scss` | keep them, at least as aliases | + +Copy-ready form of the patterns (extended regex, run over added lines only): + +```bash +git diff v2-dev...HEAD | grep '^+' | grep -En \ + -e '(^|["'"'"' .])(sm|md|lg|xl|2xl|hover|focus|active|print)(-down)?\\?:[a-z][a-z0-9-]*' \ + -e '\.(dialog|drawer)(-[a-z-]+)?\b' -e '\.menu(-item|-header|-divider)?\b' -e '\.submenu\b' \ + -e '\.btn-(solid|subtle|text|styled)\b' -e '\.badge-subtle\b' \ + -e 'data-bs-toggle="(dialog|drawer|menu|toggler)"' -e 'data-bs-dismiss="(dialog|drawer)"' \ + -e 'data-bs-(autoplay|ends|validate|bubble|modal)\b' \ + -e '\.bs\.(dialog|drawer|menu|chips|otpInput|strength|range|datepicker)\b' \ + -e '\.fg-[a-z]' -e '\.bg-[1-4]\b' -e '\.bg-(subtle|muted)-' \ + -e '\.fs-(xs|sm|md|lg|[2-6]?xl)\b' -e '\.lh-md\b' -e '\.max-[wh]-' -e '\.underline-' \ + -e '\.form-range-input\b' -e '\.form-field\b' -e '\.accordion-icon\b' +``` + +Verified 2026-09-06: this set returns zero hits on the untouched `core/scss`, `shared/ui`, +`preview/pages` and `docs/content` trees, so any hit in a diff is real. + +`.theme-*` tokens are **allowed** (they are v6 architecture) — but flag markup where a `.theme-*` +class *replaces* a v5 colour class instead of being emitted by it. `floatingConfig` is allowed once +phase 5 lands (decided in the plan). Tabler-specific classes that happen to contain these words +(`.navbar-menu`, `.dropdown-menu-*`) are not hits; check the full token, not the substring. + +## Pass 2 — silent value remaps (blockers) + +Names surviving with different values is the failure mode users cannot grep for. For any change in +`core/scss/` diff the *values* of: + +- `$spacers` (v6 remapped 0–5 to 0–9), `$sizes` +- `$border-radius*` / `$radii` and what `.rounded-N` resolves to +- `$font-sizes` / `.fs-N`, `$display-font-sizes`, `$line-height-*` +- `$grid-breakpoints` / `$breakpoints` (v6: lg 1024, xl 1280, 2xl 1536) and `$container-max-widths` +- `$theme-colors` keys, `$zindex-*` + +Compare against `git show v2-dev:` (or `dev` for the very first phase). A key that keeps its +name but changes its value is a blocker unless the PR says explicitly that the reflow is intended +and section 5 of the plan records the decision. + +## Pass 3 — public contract (blockers) + +For any touched component, confirm these still hold, by reading the code, not the description: + +- Class names emitted for every variant that existed on `dev` (`check-markup-classes` baseline is + the reference; `pnpm run check-markup-classes` if it runs in this environment). +- `data-bs-toggle` / `data-bs-dismiss` / `data-bs-target` values unchanged. +- Event names `show/shown/hide/hidden.bs.` unchanged; `hidePrevented.bs.modal` kept. +- JS: `getInstance`, `getOrCreateInstance`, `show`, `hide`, `toggle`, `dispose` present; the + `window.bootstrap`-style access documented for 1.x still works unless the ESM-only decision + (section 5) has been taken. +- `--tblr-*` custom properties that 1.x documents as public are still defined (may be aliases). +- Both `data-bs-*` and `data-tblr-*` attribute spellings still work in JS. + +## Pass 4 — proof for the claimed phase (warnings) + +Identify the phase from the branch name, PR body or commit messages, then check the required proof +is present or reproducible: + +- Phases 1, 3, 4 (output-neutral): a zero byte-diff of `core/dist/css/tabler.css` and a zero + `html-diff` run. If the PR does not show them, run what is cheap (`pnpm --filter @tabler/core run css` + then `git diff --stat` — only if no dev server is running) and report the result; otherwise + request them. +- Phase 2 (colours): a contrast-gate run and a dark-mode screenshot review listed in the PR. +- Phase 6 (native components): the a11y checklist — focus trap, Escape, backdrop click, focus + return to the trigger, `aria-*` unchanged — and a note that `html-diff` differs by design. +- Any phase: a changeset in `.changeset/` and, when a class was added or moved, updated `classnames` + front matter in `docs/content/**`. + +Missing proof is a warning, not a blocker — but say clearly what is missing. + +## Pass 5 — open questions (notes) + +If the change silently answers one of section 5's open questions (browser baseline, ESM-only, +`.form-select`, `$spacers`, component ownership of `.avatar`/`.chip`/`.stepper`, navbar-as-drawer), +note it: the decision should be recorded in the plan, not implied by code. + +## Report format + +``` +Reviewed: vs v2-dev at , phase () + +BLOCKERS +- path:line — → + +WARNINGS +- ... + +NOTES +- ... + +Contract check: classes ✓/✗ · data-bs ✓/✗ · events ✓/✗ · JS API ✓/✗ · custom props ✓/✗ +Proof: byte-diff · html-diff · a11y · changeset +``` + +Every finding cites `path:line`. Quote the offending line. Do not pad the report with things that +are fine; an empty BLOCKERS section is the goal, and "nothing found" is a valid, complete result. diff --git a/.agents/rules/docs.mdc b/.agents/rules/docs.mdc index 22ea3d57c..1e9103b14 100644 --- a/.agents/rules/docs.mdc +++ b/.agents/rules/docs.mdc @@ -51,6 +51,8 @@ description: Practical description of when and why to use it. No `layout:` key — the route picks the layout. +Quote `summary:` and `description:` values that contain a colon followed by a space (`Cards: use them…`) — unquoted, js-yaml reads the text as a nested map and the page breaks: a 500 on the dev server and a failed docs build on Vercel. Open the page on `:3010` before pushing. + Optional keys: `seoTitle`, `seoDescription`, `icon`, `order`, `related`, `docs-libs`, `css-plugins`, `hide-pagination`, `added-in`. Anything else is rejected. ### Examples diff --git a/.agents/rules/main.mdc b/.agents/rules/main.mdc index 972df2148..ffd45b9ca 100644 --- a/.agents/rules/main.mdc +++ b/.agents/rules/main.mdc @@ -7,7 +7,7 @@ alwaysApply: true # Tabler — project rules -Shared instructions for all AI agents (Claude Code, Cursor, etc.). Canonical agent configuration lives in `.agents/` (`rules/` + `skills/`); `.claude/skills` is a symlink into it. +Shared instructions for all AI agents (Claude Code, Cursor, etc.). Canonical agent configuration lives in `.agents/` (`rules/` + `skills/` + `agents/`); `.claude/{skills,agents}` and `.cursor/{rules,skills,agents}` are symlinks into it, and the root `CLAUDE.md` only `@`-imports `rules/*.mdc` for Claude Code — edit the files under `.agents/` only. Rules in `rules/*.mdc` (`main`, `docs`, `v2`) always apply. ## Project structure @@ -39,6 +39,7 @@ Task-specific instructions live in `.agents/skills//SKILL.md` — read the | `build-pipeline` | assets missing, stale or growing; editing `.build/` or the asset manifests | | `generate-changeset` | writing a changeset (always — never hand-write the file) | | `mr-description` | drafting an MR/PR title and description | +| `design-system` | adding or changing anything visible — a component, variant, token, demo page or docs example; the checklist behind the `design-reviewer` agent | ## Language diff --git a/.agents/rules/v2.mdc b/.agents/rules/v2.mdc new file mode 100644 index 000000000..9e64c9dd6 --- /dev/null +++ b/.agents/rules/v2.mdc @@ -0,0 +1,56 @@ +--- +description: Tabler 2.0 policy — target branch, what stays from Bootstrap v5, what is adopted from v6, proof required per phase +globs: +alwaysApply: true +--- + +# Tabler 2.0 — branch and compatibility policy + +Applies to every change on `v2-dev`. The plan, phases and open questions live in +`BOOTSTRAP-V6-MIGRATION.md` at the repo root — read sections 2 and 3 before starting 2.0 work. + +## Branches + +- All 2.0 work targets **`v2-dev`**. Cut feature branches from it and open PRs back into it. +- `dev` is frozen for 1.x maintenance. Never merge 2.0 work into `dev`. +- Every 2.0 PR names the phase it belongs to and carries that phase's proof (below). +- Every PR into `v2-dev` goes into the GitHub milestone **2.0**: `gh pr create --base v2-dev --milestone "2.0" …` (or `gh pr edit --milestone "2.0"` afterwards). + +## Kept from Bootstrap v5 — the public contract + +- Class names: `.modal`, `.offcanvas`, `.dropdown-menu`, `.btn-primary`, `.col-md-6`, `.d-md-none`, `.text-*`, `.fs-1`…`.fs-6`, `.rounded-*`, and every class documented for 1.x. +- `data-bs-*` attributes and their values (`data-bs-toggle="modal"`), with the `data-tblr-*` alias still working. +- Event names (`shown.bs.modal`), JavaScript plugin names and methods (`Modal.getInstance`, `show`, `hide`, `dispose`). +- The **values** behind existing utilities: `$spacers`, `.rounded-N`, `.fs-N`, breakpoints and container widths do not change silently. + +## Not adopted from v6 + +- The `md:` / `hover:` prefix syntax — infix names (`.col-md-6`) stay. Revisit no earlier than 3.0. +- Renames: `.dialog`, `.drawer`, `.menu`, `.fg-*`, `.bg-1`…`.bg-4`, `.fs-xs`…`.fs-6xl`, `.max-w-*`, `.underline-*`, `data-bs-toggle="dialog|drawer|menu"`, `*.bs.dialog|drawer|menu` events, `Dialog`/`Drawer`/`Menu` exports. +- The v6 remaps of `$spacers` (0–9) and `.rounded-*`. +- Removing `.form-select` — it stays untouched in 2.0. + +## Adopted from v6 — the architecture + +- Token files `_config` / `_colors` / `_theme` / `_root` with the `defaults()` + `tokens()` pattern; cascade `@layer`; custom properties prefixed by PostCSS, never by a Sass `$prefix`. +- `oklch()` colours, `color-mix()` scales, `.theme-*` tokens — **emitted by the v5 classes**, not replacing them in markup (`.btn-primary` sets the theme tokens; markup never needs `.btn-solid .theme-primary`). +- Logical properties in spacing and border utilities (class names unchanged). +- Native `` for modal/offcanvas, `
` for accordion, CSS scroll snap for carousel — under the v5 names, events and data attributes. +- Floating UI instead of Popper; one `focus-ring()` mixin; the numeric `$radii` scale mapped so `.rounded-N` keeps its v5 values. +- ESM-only JavaScript: no UMD bundle, no `window.tabler` global in 2.0. +- Component parity with v6 for `.avatar`, `.chip`, `.stepper`, `.progress`, `.status`: Tabler's versions are reworked to match the v6 class names, modifiers and tokens; a Tabler-only name such as `.steps` stays as an alias for one major. This is the one agreed exception to "every 1.x class stays" — see phase 9 of the plan. +- The internal Sass API (variable names, maps, mixins, file layout) is **not** promised and may change, as long as compiled output stays as intended. + +## Proof per phase + +- Output-neutral phases (1 Sass split, 3 token scales, 4 logical properties): byte-identical `core/dist/css/tabler.css` and a zero `html-diff` run — see the `html-diff` skill. +- Phase 2 (colours): contrast gate passed, dark mode reviewed by screenshot. +- Phase 6 (native components): the a11y checklist — focus trap, Escape, backdrop click, focus returns to the trigger, `aria-*` unchanged — and a note that `html-diff` differs by design. +- Every phase: a changeset (`generate-changeset` skill), updated `classnames` front matter when a class moved (`class-reference` skill). + +## Agents + +- Before opening a PR into `v2-dev`, run the `v2-policy-reviewer` agent on the diff and fix its blockers. +- When the diff touches something visible (`core/scss/ui/**`, `shared/ui/**`, `preview/pages/**`, a docs example), also run the `design-reviewer` agent; it checks the change against the `design-system` skill and reports known deviations by number. +- To learn how Bootstrap v6 implements something, ask the `bootstrap-v6-reference` agent; it reads the upstream source and reports mechanism, not names. +- The open questions in section 5 of the plan are decided by the maintainer and recorded in the plan — never implied by code. diff --git a/.agents/skills/astro-dev/SKILL.md b/.agents/skills/astro-dev/SKILL.md index 7edec33d1..f16bc7e3f 100644 --- a/.agents/skills/astro-dev/SKILL.md +++ b/.agents/skills/astro-dev/SKILL.md @@ -23,6 +23,8 @@ Never ask the user to check a page manually. Start the server, look at the page, `preview_start` returns a `tabId` — pass it to every later `navigate` / `read_page` / `computer` call so they act on that tab and not on whatever is fronted. The same entries exist in `.claude/launch.json` (`preview`, `docs`, `screenshots`) — start them with `preview_start` so the browser pane opens on the right port, and use `preview-attach` when a server is already running. `pnpm dev` at the repo root starts everything through turbo; only do that when the change spans packages, because the persistent tasks make the output hard to read. +**Port 3000 belongs to the main checkout.** In a git worktree the ports above are taken, and the worktree has no built assets yet. Prepare once with `pnpm turbo run dev:prepare --filter @tabler/preview` (builds `core` and copies the assets into the worktree's `public/`), then start `pnpm --dir preview exec astro dev --port 3001` — the `preview-alt` entry in `.claude/launch.json`. An `html-diff` baseline must be captured from the same server as the comparison, because the toolbar embeds the server path in the HTML. + A `shared/` change shows up in both preview and docs. Verify in the package the user asked about, and open the other one too when the component is used there. ## 2. Full dev vs. astro-only diff --git a/.agents/skills/core-js/SKILL.md b/.agents/skills/core-js/SKILL.md index 77517bf53..61619f565 100644 --- a/.agents/skills/core-js/SKILL.md +++ b/.agents/skills/core-js/SKILL.md @@ -18,8 +18,10 @@ description: >- | Entry | Ships as | Holds | | --- | --- | --- | -| `js/tabler.ts` | `tabler.js` / `.esm.js` (+ `.min`) | plugin initialisers, the Bootstrap components, the `tabler` helper namespace | -| `js/tabler-theme.ts` | `tabler-theme.js` (+ variants) | the colour-mode/theme switcher only | +| `js/tabler.ts` | `tabler.js` (+ `.min`) | plugin initialisers, the Bootstrap components, the `tabler` helper exports | +| `js/tabler-theme.ts` | `tabler-theme.js` (+ `.min`) | the colour-mode/theme switcher only | + +Both bundles are **ESM only** (Tabler 2.0, like Bootstrap v6): no UMD, no `window.tabler` global. `tabler.js` is loaded with ` + ``` @@ -106,7 +106,7 @@ The `@tabler/core` package contains compiled and minified CSS and JavaScript, th ├── dist/ │ ├── css/ tabler.css and the optional stylesheets (flags, marketing, payments, socials, │ │ themes, vendors), each with .min and .rtl versions -│ ├── js/ tabler.js and tabler.esm.js, plus the standalone tabler-theme.js +│ ├── js/ tabler.js (ESM, load with + ``` If you don't use a package manager, [download the latest release](https://github.com/tabler/tabler/releases) as a ZIP archive. @@ -46,7 +46,8 @@ import '@tabler/core/dist/css/tabler.min.css' import '@tabler/core/dist/js/tabler.min.js' ``` -There's also an ES module build with named exports, which is handy when you create components from code: +The bundle is ESM only — in a plain HTML page load it with ` + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dbba8ce04..d9a16898a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,9 +112,9 @@ importers: core: dependencies: - '@popperjs/core': - specifier: ^2.11.8 - version: 2.11.8 + '@floating-ui/dom': + specifier: ^1.8.0 + version: 1.8.0 devDependencies: '@melloware/coloris': specifier: ^0.25.0 @@ -928,6 +928,15 @@ packages: cpu: [x64] os: [win32] + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@fullcalendar/core@6.1.21': resolution: {integrity: sha512-t3u/+sqh3Iq7TWtUnVLcGDUE6OWZh0UD3c04bI/l7lSLAgAKr3kngBmhHiQD1QXpwC8ZN5iNqG7a7gOVixhSKQ==} @@ -1328,9 +1337,6 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@popperjs/core@2.11.8': - resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@prettier/sync@0.6.1': resolution: {integrity: sha512-yF9G8vK/LYUTF3Cijd7VC9La3b20F20/J/fgoR4H0B8JGOWnZVZX6+I6+vODPosjmMcpdlUV+gUqJQZp3kLOcw==} peerDependencies: @@ -5580,6 +5586,17 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/utils@0.2.12': {} + '@fullcalendar/core@6.1.21': dependencies: preact: 10.12.1 @@ -5930,8 +5947,6 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@popperjs/core@2.11.8': {} - '@prettier/sync@0.6.1(prettier@3.9.5)': dependencies: make-synchronized: 0.8.0 diff --git a/preview/pages/2-step-verification-code.astro b/preview/pages/2-step-verification-code.astro index b01e63639..0ff883270 100644 --- a/preview/pages/2-step-verification-code.astro +++ b/preview/pages/2-step-verification-code.astro @@ -7,10 +7,10 @@ import CaptureScript from '@shared/components/CaptureScript.astro' import CardTitle from '@ui/CardTitle.astro' --- - +
- Authenticate Your Account + Authenticate your account

Please confirm your account by entering the authorization code sent to +1 856-672-8552. diff --git a/preview/pages/2-step-verification.astro b/preview/pages/2-step-verification.astro index 1c28f33c7..aeb842571 100644 --- a/preview/pages/2-step-verification.astro +++ b/preview/pages/2-step-verification.astro @@ -7,10 +7,10 @@ import flags from '@data/flags.json' import CardTitle from '@ui/CardTitle.astro' --- - +

- 2-Step Verification + 2-step verification - +
+1 diff --git a/preview/pages/all-elements.astro b/preview/pages/all-elements.astro index 6eef14e1c..6100dacfb 100644 --- a/preview/pages/all-elements.astro +++ b/preview/pages/all-elements.astro @@ -70,7 +70,7 @@ function greetUser(name) { }` --- - +
@@ -92,7 +92,7 @@ function greetUser(name) { This is a regular paragraph with bold text, italic text, and underlined text.

-

This is small muted text.

+

This is small muted text.

Primary text color

Success text color

Danger text color

@@ -109,7 +109,7 @@ function greetUser(name) {
-

Standard Buttons

+

Standard buttons

-

Button Sizes

+

Button sizes

- + + @@ -627,7 +627,7 @@ function greetUser(name) { @@ -672,18 +672,18 @@ function greetUser(name) {
- + -

Basic Button Group

+

Basic button group

-

Button Toolbar

-
{xmrBtc} - {xmr.price} + {xmr.price}
- Volume: {xmr['volume-24h'].replace('$', '')} + Volume: {xmr['volume-24h'].replace('$', '')}
@@ -261,7 +261,7 @@ const operationCurrencies = currencies.slice(0, 20)
-

The final amount could change depending on current market conditions.

+

The final amount could change depending on current market conditions.

@@ -269,7 +269,7 @@ const operationCurrencies = currencies.slice(0, 20)
- Sell Orders + Sell orders @@ -300,7 +300,7 @@ const operationCurrencies = currencies.slice(0, 20)
- Buy Orders + Buy orders diff --git a/preview/pages/datatables.astro b/preview/pages/datatables.astro index 6970142b5..8a448b021 100644 --- a/preview/pages/datatables.astro +++ b/preview/pages/datatables.astro @@ -4,7 +4,6 @@ import { toUnixSeconds, formatLongDate } from '@shared/lib/date-format' import { randomNumber, randomDate } from '@shared/lib/pseudo-random' import Card from '@ui/Card.astro' import CardHeader from '@ui/CardHeader.astro' -import CardBody from '@ui/CardBody.astro' import CardTitle from '@ui/CardTitle.astro' import CardSubtitle from '@ui/CardSubtitle.astro' import Progress from '@ui/Progress.astro' @@ -44,47 +43,45 @@ const valueNames = ['sort-name', 'sort-type', 'sort-city', 'sort-score', { attr: Click a column header to sort the rows by that value.
- -
- - - - - - - - - - - - - - { - rows.map(({ rc, progress, quantity, dateSeconds, dateLabel }) => ( - - - - - - - - + + )) + } + +
{rc.name}{rc.city}{rc.type}{rc.score} - {dateLabel} - {quantity} -
-
{progress}%
-
- -
+
+ + + + + + + + + + + + + + { + rows.map(({ rc, progress, quantity, dateSeconds, dateLabel }) => ( + + + + + + + + - - )) - } - -
{rc.name}{rc.city}{rc.type}{rc.score} + {dateLabel} + {quantity} +
+
{progress}%
+
+
-
-
- +
+
+
diff --git a/preview/pages/dropzone.astro b/preview/pages/dropzone.astro index 5c7e7e6e0..c9a613ca0 100644 --- a/preview/pages/dropzone.astro +++ b/preview/pages/dropzone.astro @@ -14,7 +14,7 @@ import DocsLink from '@ui/DocsLink.astro'
- Basic Usage + Basic usage Drag a file in or click to browse — a single drop target for one upload. @@ -23,7 +23,7 @@ import DocsLink from '@ui/DocsLink.astro'
- Multiple File Upload + Multiple file upload Accept and preview several files at once from the same drop target. @@ -32,7 +32,7 @@ import DocsLink from '@ui/DocsLink.astro'
- Custom Dropzone + Custom dropzone Swap in your own label and helper text for the drop target. diff --git a/preview/pages/email-inbox.astro b/preview/pages/email-inbox.astro index b834eb70b..a056aa8dc 100644 --- a/preview/pages/email-inbox.astro +++ b/preview/pages/email-inbox.astro @@ -1,4 +1,5 @@ --- +import Empty from '@ui/Empty.astro' import Subheader from '@ui/Subheader.astro' import ButtonGroup from '@ui/ButtonGroup.astro' import DefaultLayout from '@shared/layouts/DefaultLayout.astro' @@ -16,7 +17,7 @@ import { formatShortDate } from '@shared/lib/date-format' import mails from '@data/mails.json' // Label colors shared by the folder sidebar and the dots in the message list. -const labelColors: Record = { Updates: 'info', Friends: 'warning', Family: 'success', Social: 'primary', Important: 'danger', Promotions: 'purple' } +const labelColors: Record = { Updates: 'azure', Friends: 'orange', Family: 'teal', Social: 'indigo', Important: 'pink', Promotions: 'purple' } --- @@ -37,7 +38,7 @@ const labelColors: Record = { Updates: 'info', Friends: 'warning Starred Snoozed Draft32 - Sent Mail + Sent mail Trash Important Spam @@ -99,7 +100,7 @@ const labelColors: Record = { Updates: 'info', Friends: 'warning
-
- - { - mails.length > 0 ? ( - mails.map((mail) => ( - -
-
- -
-
- - - -
- -
- - {mail.subject} - - – {mail.preview} -
-
- {mail.labels.map((label) => ( - - ))} -
- -
{formatShortDate(new Date(mail.date))}
+ + { + mails.length > 0 ? ( + mails.map((mail) => ( + +
+
+
- - )) - ) : ( - No emails - ) - } - -
-
+
+ + + +
+ +
+ + {mail.subject} + + – {mail.preview} +
+
+ {mail.labels.map((label) => ( + + ))} +
+ +
{formatShortDate(new Date(mail.date))}
+
+
+ )) + ) : ( + + + + ) + } +
+