Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
applyTo: "**"
---

# OUDS Web

**OUDS Web** (Orange Unified Design System — Web) is a multi-brand design system built as a fork of Bootstrap 5.3.6. It serves 3 brands (Orange, Sosh, Orange Compact) from a single codebase — only CSS tokens differ per brand, JavaScript is fully shared.

## Quick Reference

| Key | Value |
|---|---|
| Version | 1.1.0 |
| Main branch | `ouds/main` |
| CSS prefix | `--bs-` (via `$prefix`) |
| Build | `npm run dist` |
| Lint | `npm run lint` |
| Tests | `npm run test` |
| Dev servers | `:9001` (orange) · `:9002` (sosh) · `:9003` (orange-compact) |

## Skills

This project provides specialized skills in `.github/skills/`. Invoke them when relevant — they contain detailed conventions, workflows, and reference material:

- **scss-conventions** — SCSS rules (variables, mixins, tokens, forbidden patterns)
- **javascript-conventions** — JS rules (no-semi, BaseComponent, imports)
- **accessibility** — WCAG 2.2 AAA/AA, ARIA, focus, contrast, touch targets
- **token-system** — 3-tier token hierarchy, dark mode, auto-gen protections
- **component-patterns** — CSS custom props, state overrides, markers
- **multi-brand** — 3-brand architecture, cross-brand sync
- **create-component** — Step-by-step workflow for new components
- **diagnose-errors** — Error diagnostic decision tree
- **validate-tokens** — Read-only token audit
- **code-review** — Compliance checker (safety net before commit/PR)
- **project-knowledge** — Glossary, npm scripts, CI/CD, file map
21 changes: 21 additions & 0 deletions .github/instructions/critical-rules.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
applyTo: "**"
---

# Critical Rules (Non-Negotiable)

These 7 rules apply to every file in OUDS Web. They are the minimum safety net — always enforced regardless of which skills are loaded.

1. **Tokens over hardcoding** — Never use hardcoded colors (`#ff7900`), spacing (`16px`), or dimensions. Use `$ouds-*` tokens or `var(--#{$prefix}color-*)`.

2. **Mixins for border-radius and transition** — Direct properties are Stylelint violations. Use `@include border-radius()` and `@include transition()`.

3. **Auto-generated token files are read-only** — Never edit `_raw.scss`, `_semantic.scss`, `_component.scss`, or `_*-custom-props.scss`. Only `_composite.scss` is hand-editable.

4. **Never commit dist/** — Build artifacts are generated in CI.

5. **JS in js/src/ only** — Never put JavaScript in `packages/<brand>/`. JS is shared across all brands.

6. **Colors via CSS custom properties** — Use `var(--#{$prefix}color-*)` for all colors. This enables dark mode switching at runtime.

7. **Accessibility is mandatory** — Every interactive element needs: `@include focus-visible()`, correct ARIA attributes, `.visually-hidden` (never `display: none`) for SR content, 44×44px touch targets.
84 changes: 84 additions & 0 deletions .github/skills/accessibility/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
name: 'accessibility'
description: 'WCAG 2.2 accessibility rules for OUDS Web. Use this skill when creating or editing interactive components, writing HTML templates, adding ARIA attributes, handling focus states, or reviewing accessibility. Covers WCAG 2.2 Level AAA target (AA fallback), ARIA patterns for every component type, focus-visible mixin, color contrast (7:1 AAA / 4.5:1 AA), 44×44px touch targets, reduced motion, RTL, focus trapping, and cognitive accessibility. Always invoke this skill for any work touching user interaction or markup structure.'
---

# Accessibility (WCAG 2.2)

## Compliance target

**WCAG 2.2 Level AAA** primary, with **Level AA as documented fallback** when brand constraints make AAA infeasible. Every component meets at least AA.

---

## ARIA patterns (quick reference)

| Pattern | Required ARIA |
|---|---|
| Decorative icons | `aria-hidden="true"` on `<svg>` |
| Close buttons | `aria-label="Close"` or `aria-labelledby` |
| Toggles | `aria-pressed="true/false"` |
| Expandable | `aria-expanded` + `aria-controls="id"` |
| Form validation | `aria-invalid="true"` + `aria-describedby` |
| Disabled | `aria-disabled="true"` |
| Loading | `aria-busy="true"` |
| Icon-only buttons | `aria-label="Action"` + `aria-hidden` on icon |

---

## Focus management

- Use `@include focus-visible()` mixin — never `outline: none` without alternative
- Focus indicators: 3:1 contrast minimum, 2px thick (AAA: 3-4px)
- Modal/offcanvas: trap focus via `focustrap.js`, Escape closes, focus returns to trigger
- Focused elements must not be obscured by sticky headers or overlays

---

## Color contrast

| Content | AAA target | AA fallback |
|---|---|---|
| Text | 7:1 | 4.5:1 |
| UI components | 4.5:1 | 3:1 |

When AA fallback needed: use larger/bolder text, redundant visual cues (never color alone), document with `// OUDS mod: AA fallback — [reason]`.

---

## Touch targets (WCAG 2.5.8)

Minimum 44×44px on all interactive elements. Use `@include target-size()` mixin.

---

## Reduced motion (WCAG 2.3.3)

All animation respects `prefers-reduced-motion: reduce`. The `@include transition()` mixin handles this — which is why direct `transition:` is forbidden.

---

## Semantic HTML

- `<button>` for actions, `<a>` for navigation — never `<div>`/`<span>` as interactive
- Heading hierarchy: `<h1>` → `<h2>` → `<h3>` (never skip)
- `role="alert"` for auto-announced; `role="status"` for polite
- `.visually-hidden` for SR content — never `display: none`

---

## Color modes

Use `data-bs-theme` attribute, never `prefers-color-scheme` media queries.

---

## WCAG 2.2 specifics

| Criterion | Requirement |
|---|---|
| Consistent Help (3.2.6) | Help mechanisms in same order across pages |
| Accessible Auth (3.3.8/9) | Support `autocomplete`, offer passkey/magic-link |
| Dragging (2.5.7) | Click/keyboard alternative to all drag |
| Redundant Entry (3.3.7) | Auto-populate previously entered data |
| Focus Not Obscured (2.4.11/12) | Never fully hidden by sticky elements |
88 changes: 88 additions & 0 deletions .github/skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
name: 'code-review'
description: 'OUDS Web compliance code review. Use this skill when reviewing a diff, preparing a commit, or when asked to check code quality. Acts as a safety net that verifies all OUDS conventions are respected: token usage (no hardcoded values, no raw tokens), required mixins (border-radius, transition), accessibility (ARIA, focus-visible), multi-brand sync (tokens in all 3 brands), and file hygiene (no dist/, no auto-gen edits). Invoke proactively before any commit or PR submission.'
---

# Code Review (OUDS Compliance)

## Purpose

This skill is a safety net. It catches OUDS convention violations that other skills might have missed. Invoke it before committing or when reviewing diffs.

---

## Checklist

For each changed file in the diff, verify:

### SCSS files (`**/*.scss`)

- [ ] **No hardcoded values** — colors (`#xxx`), spacing (`16px`), dimensions must use tokens
- [ ] **No raw tokens** — `$core-ouds-*`, `$core-orange-*`, `$core-sosh-*` never in component SCSS
- [ ] **Mixins used** — `@include border-radius()` and `@include transition()` (never direct properties)
- [ ] **`border: 0`** — not `border: none`
- [ ] **No `lighten()`/`darken()`** — use token variant
- [ ] **`!default` on variables** — every module-scope SCSS variable
- [ ] **Colors via CSS custom props** — `var(--#{$prefix}color-*)` for dark mode
- [ ] **`color-mode()` mixin** — not raw `[data-bs-theme]` or `prefers-color-scheme`
- [ ] **`#{$ouds-root-selector}`** — not `:root` directly
- [ ] **`// OUDS mod:`** comment on Bootstrap deviations

### JavaScript files (`js/src/**/*.js`)

- [ ] **No semicolons**
- [ ] **No trailing commas**
- [ ] **Template literals** (not concatenation)
- [ ] **`.js` extension** on imports
- [ ] **No `console.*`** in `js/src/`
- [ ] **`'use strict'`** at top
- [ ] **Extends `BaseComponent`** (if it's a component)

### HTML/MDX files

- [ ] **Semantic elements** — `<button>` for actions, `<a>` for links
- [ ] **ARIA attributes** — correct for the interaction pattern
- [ ] **`.visually-hidden`** — not `display: none` for SR content
- [ ] **`data-bs-theme`** — not `prefers-color-scheme` for color modes

### Token/brand files (`packages/**`)

- [ ] **Auto-generated files not modified** — `_raw.scss`, `_semantic.scss`, `_component.scss`, `_*-custom-props.scss`
- [ ] **New tokens in all 3 brands** — if a new `$ouds-<comp>-*` was added
- [ ] **Import order preserved** — in `_index.scss`

### General

- [ ] **No `dist/` files** in the diff
- [ ] **No JS in `packages/<brand>/`** — JS is shared
- [ ] **Focus visible** — `@include focus-visible()` on interactive elements
- [ ] **Touch targets** — 44×44px minimum on new interactive elements

---

## How to use

1. Run `git diff --stat` to see changed files
2. For each file category, apply the relevant checklist section
3. Report violations with file, line, and suggested fix
4. Classify: ERROR (must fix) vs WARNING (should fix)

---

## Report format

```
## Code Review: OUDS Compliance

### ❌ Errors (must fix before merge)
- `scss/_buttons.scss:42` — hardcoded `padding: 8px` → use `$ouds-space-*` token
- `js/src/tooltip.js:15` — semicolon present → remove

### ⚠️ Warnings (should fix)
- `scss/_card.scss:88` — missing `// OUDS mod:` on Bootstrap override

### ✅ Passed
- Token cross-brand check: all present
- No dist/ files in diff
- ARIA attributes correct
```
113 changes: 113 additions & 0 deletions .github/skills/component-patterns/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
name: 'component-patterns'
description: 'SCSS and JS structural patterns for OUDS Web components. Use this skill when creating, editing, or reviewing component SCSS files (scss/_*.scss, scss/forms/_*.scss) or JS component files (js/src/*.js). Covers CSS custom property declarations at top of class, state overrides via variable overrides (not property overrides), scss-docs-start/end markers, border-width compensation with calc(), mask-image icon pattern, responsive breakpoint overrides, and the OUDS mod comment convention.'
---

# Component Patterns

## Core principle

Every component declares CSS custom properties at the top of its main selector, then consumes them via `var()`. State changes (hover, disabled, variants) override the *variable*, never the *property*. This creates one source of truth and enables runtime theming.

---

## SCSS structure template

```scss
#{$ouds-root-selector} {
--#{$prefix}my-comp-border-radius: #{$ouds-border-radius-default};
}

.my-component {
// scss-docs-start my-component-css-vars
--#{$prefix}my-comp-padding: #{$ouds-my-comp-space-padding-block};
--#{$prefix}my-comp-color: #{$ouds-color-content-default};
--#{$prefix}my-comp-bg: #{$ouds-color-bg-primary};
// scss-docs-end my-component-css-vars

padding: var(--#{$prefix}my-comp-padding);
color: var(--#{$prefix}my-comp-color);
background-color: var(--#{$prefix}my-comp-bg);
@include border-radius(var(--#{$prefix}my-comp-border-radius));
@include transition($transition-base);

&:hover {
--#{$prefix}my-comp-bg: #{$ouds-color-bg-secondary}; // Override var, not property
}

&:focus-visible {
@include focus-visible();
}

&:disabled, &[aria-disabled="true"] {
--#{$prefix}my-comp-color: #{$ouds-color-content-disabled};
pointer-events: none;
}
}

.my-component-variant {
--#{$prefix}my-comp-bg: #{$ouds-color-surface-brand};
}
```

---

## Key patterns

### Documentation markers

The Astro docs site extracts CSS variable tables from these markers. Every component needs them:

```scss
// scss-docs-start badge-css-vars
// scss-docs-end badge-css-vars
```

### Border-width compensation

Prevent layout shift when borders change on interaction:

```scss
padding: calc(var(--#{$prefix}btn-padding-y) - var(--#{$prefix}btn-border-width))
calc(var(--#{$prefix}btn-padding-x) - var(--#{$prefix}btn-border-width));
```

### Icon pattern (mask-image)

Icons inherit color from `currentcolor`:

```scss
&::before {
content: "";
background-color: currentcolor;
mask-image: escape-svg(var(--#{$prefix}icon));
mask-size: 100%;
mask-repeat: no-repeat;
}
```

### Responsive overrides

Override the variable at breakpoints, don't duplicate rulesets:

```scss
.component {
--#{$prefix}comp-width: #{$mobile};
@include media-breakpoint-up(xl) {
--#{$prefix}comp-width: #{$desktop};
}
}
```

---

## OUDS mod markers

Every deviation from Bootstrap upstream must be marked:

```scss
display: flex; // OUDS mod: instead of `inline-block`
// OUDS mod: loading buttons
.btn.loading-indeterminate { ... }
// End mod
```
Loading
Loading