ADR-040 ActifActive contract

Implementing `agtc-table`

DateDate
2026-06-03
DécideursDecision makers
Design System Lead

Reference UX patterns applied

Added 2026-06-03 via the ux-pattern-review workflow (ADR-036). Decision: T1–T10 all approved. Detail and links: guidelines/components/table.md § REFERENCE UX PATTERNS.

#PatternSource
T1Semantic HTML + scope="col"Smashing — Table Patterns
T2<caption> (hideable)Smashing
T3Text left-aligned, numeric right-alignedNN/g — Data Tables
T4Row separators (optional zebra striping)NN/g
T5Row hoverNN/g
T6Sticky header (optional)NN/g · Smashing
T7Horizontal scroll + overflow indicatorSmashing
T81st column = readable identifierNN/g
T9compact density by defaultDashboard Design Patterns
T10Sort/filter/pagination — door left open, out of v1NN/g

Context

agtc-table is the most used component on the generated site: 692 occurrences of token-row/token-table (token tables: CSS token → reference → value → intent), plus comparison tables and DOs/DON'Ts tables. The gap analysis of 2026-06-03 identified it as the first component to build from the site's actual needs.

Observation at creation time: the site's original HTML has neither scope on <th> nor <caption> — an accessibility gap this component fills.

The table is read-only: documentation tables don't need editing, sorting, or pagination. We avoid speculative design while keeping the door open.


Decisions

Decision 1 — "Mix" architecture: data-driven component + light-DOM class

Two forms consuming the same component.table.* tokens:

1. <agtc-table> (Lit, shadow DOM) — driven by .columns/.rows. For apps, JS contexts, and Storybook. Consistent with other components (shadow DOM + static styles). 2. .agtc-table (CSS class) — applied to a real, hand-written <table>. For the static site, which must remain resilient: displaying a table must not depend on JS.

Why not a single mechanism?

  • An all-shadow-DOM data-driven approach would make the site JS-dependent to display

its tables — a resilience regression (the site is currently pure static HTML).

  • *Slotting* a full <table> into a shadow DOM doesn't allow styling th/td

(::slotted doesn't reach descendants). The light-DOM class works around this limitation and handles rich cell content (e.g., <code>), which the data-driven API (escaped text) doesn't cover.

Decision 2 — Read-only; sort/filter/pagination out of scope, but an extensible API

v1 implements neither sorting, filtering, nor pagination (T10). The site's actual need is static tables. But a future need was confirmed by the user: the columns/rows API is chosen precisely to accommodate them without a breaking change (future column.sortable, @sort event, toolbar slot). No dead property ships in v1, to avoid a misleading API.

Decision 3 — table tokens in component.json, default.* structure + paddings

Colors and radius live under table.default.*; paddings (horizontal, compact/comfortable vertical) sit at the table.* level. This separation allows changing density without touching colors. Compliant with the three levels (ADR-001): every token points to a semantic token (background.subtle, border.default, background.hover…), no raw value.

Decision 4 — Accessible by default: scope, <caption>, numeric alignment

<th scope="col"> systematically; <caption> recommended (console warning if absent), visually hideable via caption-hidden; numeric columns right-aligned (align="end") for vertical scanning. Fills the gaps in the original HTML.

Decision 5 — Row separators by default, zebra striping optional (T4)

User decision: row separators by default (understated, suited to dense token tables), zebra striping available via the striped attribute.

Decision 6 — compact density by default (T9)

Token tables are dense; compact is the default, comfortable available via density.


v1 scope

IncludedExcluded (door left open)
Data-driven component (columns/rows)Sort / filter / pagination
.agtc-table class (light DOM, rich content)Inline cell editing
caption + caption-hidden, scope="col"Row selection / bulk actions
Per-column alignment (start/end/center)Resizable / reorderable columns
striped, sticky-header, densityMulti-level header / colspan
Horizontal scroll + overflow indicatorRich HTML cells via the data-driven API (→ use the class)

Rejected alternatives

  • All-shadow-DOM data-driven only: makes the site JS-dependent for its tables (regression).
  • All-CSS-class only: no data-driven API for apps; no clean Storybook story.
  • Slotting a <table> into shadow DOM: impossible to style slotted th/td.
  • Shipping a no-op sortable to "flag" T10: a misleading API; documenting extensibility preferred instead.

Consequences

  • The site will be able to migrate its token-table/token-row elements to

.agtc-table during the *dogfooding* step (category A of the gap analysis), with no JS dependency.

  • Any future addition of sort/filter/pagination will require a new ADR (governance).

Tokens added to component.json

TokenSemantic reference
--agtc-table-default-header-backgroundsemantic.color.background.subtle
--agtc-table-default-header-textsemantic.color.text.secondary
--agtc-table-default-cell-textsemantic.color.text.primary
--agtc-table-default-bordersemantic.color.border.default
--agtc-table-default-row-hoversemantic.color.background.hover
--agtc-table-default-stripesemantic.color.background.subtle
--agtc-table-default-caption-textsemantic.color.text.secondary
--agtc-table-default-radiussemantic.radius.card
--agtc-table-default-font-sizesemantic.typography.label.size
--agtc-table-padding-xprimitive.space.3
--agtc-table-padding-y-compactprimitive.space.2
--agtc-table-padding-y-comfortableprimitive.space.3

Patterns UX de référence appliqués

Ajouté le 2026-06-03 via le workflow ux-pattern-review (ADR-036). Décision : T1–T10 tous approuvés. Détail et liens : guidelines/components/table.md § PATTERNS UX DE RÉFÉRENCE.

#PatternSource
T1HTML sémantique + scope="col"Smashing — Table Patterns
T2<caption> (masquable)Smashing
T3Alignement texte/gauche, numérique/droiteNN/g — Data Tables
T4Séparateurs de lignes (zébrage en option)NN/g
T5Survol de ligneNN/g
T6En-tête figé (optionnel)NN/g · Smashing
T7Scroll horizontal + indicateur d'overflowSmashing
T81ʳᵉ colonne = identifiant lisibleNN/g
T9Densité compact par défautDashboard Design Patterns
T10Tri/filtre/pagination — porte ouverte, hors v1NN/g

Contexte

agtc-table est le composant le plus utilisé du site généré : 692 occurrences de token-row/token-table (tables de tokens : token CSS → référence → valeur → intention), plus les tables de comparaison et les tables DOs/DON'Ts. La gap-analysis du 2026-06-03 l'a identifié comme premier composant à construire à partir des besoins réels du site.

Constat à la création : le HTML d'origine du site n'a ni scope sur les <th> ni <caption> — un trou d'accessibilité que ce composant comble.

La table est en lecture seule : les tables documentaires n'ont pas besoin d'édition, de tri ni de pagination. On évite la conception spéculative tout en gardant la porte ouverte.


Décisions

Décision 1 — Architecture « mix » : composant piloté par données + classe light DOM

Deux formes consommant les mêmes tokens component.table.* :

1. <agtc-table> (Lit, shadow DOM) — piloté par .columns/.rows. Pour les apps, les contextes JS et Storybook. Cohérent avec les autres composants (shadow DOM + static styles). 2. .agtc-table (classe CSS) — appliquée à un <table> réel écrit à la main. Pour le site statique, qui doit rester résilient : afficher une table ne doit pas dépendre de JS.

Pourquoi pas un seul mécanisme ?

  • Tout-shadow-DOM piloté par données rendrait le site JS-dépendant pour afficher ses tables —

régression de résilience (le site est aujourd'hui du HTML statique pur).

  • Le *slotting* d'un <table> complet dans un shadow DOM ne permet pas de styler th/td

(::slotted n'atteint pas les descendants). La classe light DOM contourne cette limite et gère le contenu riche des cellules (ex. <code>), que l'API par données (texte échappé) ne couvre pas.

Décision 2 — Lecture seule ; tri/filtre/pagination hors périmètre, mais API extensible

La v1 n'implémente ni tri, ni filtrage, ni pagination (T10). Besoin réel du site = tables statiques. Mais l'utilisateur a confirmé un besoin futur : l'API columns/rows est choisie précisément pour les accueillir sans rupture (futur column.sortable, événement @sort, slot de barre d'outils). Aucune propriété morte n'est livrée en v1 pour éviter une API trompeuse.

Décision 3 — Tokens table dans component.json, structure default.* + paddings

Les couleurs et le rayon vivent sous table.default.* ; les paddings (horizontal, vertical compact/confortable) sont au niveau table.*. Cette séparation permet de changer la densité sans toucher aux couleurs. Conforme aux trois niveaux (ADR-001) : tous les tokens pointent vers des tokens sémantiques (background.subtle, border.default, background.hover…), aucune valeur brute.

Décision 4 — Accessibilité par défaut : scope, <caption>, alignement numérique

<th scope="col"> systématique ; <caption> recommandé (avertissement console si absent), masquable visuellement via caption-hidden ; colonnes numériques alignées à droite (align="end") pour le scan vertical. Comble les trous du HTML d'origine.

Décision 5 — Séparateurs par défaut, zébrage en option (T4)

Décision utilisateur : séparateurs de lignes par défaut (sobre, adapté aux tables de tokens denses), zébrage disponible via l'attribut striped.

Décision 6 — Densité compact par défaut (T9)

Les tables de tokens sont denses ; compact est le défaut, comfortable disponible via density.


Périmètre v1

InclusExclu (porte ouverte)
Composant piloté par données (columns/rows)Tri / filtrage / pagination
Classe .agtc-table (light DOM, contenu riche)Édition de cellule en ligne
caption + caption-hidden, scope="col"Sélection de lignes / actions de masse
Alignement par colonne (start/end/center)Colonnes redimensionnables / réordonnables
striped, sticky-header, densityEn-tête multi-niveaux / colspan
Scroll horizontal + indicateur d'overflowCellules HTML riches via l'API par données (→ classe)

Alternatives rejetées

  • Tout-shadow-DOM piloté par données seul : rend le site JS-dépendant pour ses tables (régression).
  • Tout-classe CSS seul : pas d'API par données pour les apps ; pas de story Storybook propre.
  • Slotting d'un <table> dans le shadow DOM : impossible de styler th/td slottés.
  • Livrer un sortable no-op pour « marquer » T10 : API trompeuse ; préféré documenter l'extensibilité.

Conséquences

  • Le site pourra migrer ses token-table/token-row vers .agtc-table lors de l'étape de

*dogfooding* (catégorie A de la gap-analysis), sans dépendance JS.

  • Tout ajout de tri/filtre/pagination devra créer un nouvel ADR (gouvernance).

Tokens ajoutés dans component.json

TokenRéférence sémantique
--agtc-table-default-header-backgroundsemantic.color.background.subtle
--agtc-table-default-header-textsemantic.color.text.secondary
--agtc-table-default-cell-textsemantic.color.text.primary
--agtc-table-default-bordersemantic.color.border.default
--agtc-table-default-row-hoversemantic.color.background.hover
--agtc-table-default-stripesemantic.color.background.subtle
--agtc-table-default-caption-textsemantic.color.text.secondary
--agtc-table-default-radiussemantic.radius.card
--agtc-table-default-font-sizesemantic.typography.label.size
--agtc-table-padding-xprimitive.space.3
--agtc-table-padding-y-compactprimitive.space.2
--agtc-table-padding-y-comfortableprimitive.space.3
← ADR-039 ADR-041 →