diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 63902851..af5080ae 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -90,7 +90,7 @@ { "name": "bitwarden-design-tools", "source": "./plugins/bitwarden-design-tools", - "version": "0.1.0", + "version": "0.2.0", "description": "Design toolkit for Bitwarden — non-persona skills for the design lifecycle. Content style guide reference, Figma Dev Mode MCP usage, Bitwarden brand application, design-to-engineering handoff prep, Design System governance, and the Product and Design Jira workflow. Composed by the bitwarden-designer agent and usable standalone." } ] diff --git a/.cspell.json b/.cspell.json index 7f702a63..131f44b5 100644 --- a/.cspell.json +++ b/.cspell.json @@ -18,6 +18,7 @@ "bwi", "boardId", "bwwl", + "Cantarell", "catchable", "checkmarx", "CLAUDECODE", @@ -25,7 +26,9 @@ "codeBlock", "CODEOWNERS", "Confluence", + "Consolas", "CQL", + "ctfassets", "customfield", "cvss", "Dashlane", @@ -36,6 +39,7 @@ "ECDH", "ECIES", "editui", + "evenodd", "ESL", "evals", "excalidraw", @@ -68,8 +72,11 @@ "keyserver", "lockdown", "lockfiles", + "lockup", + "lockups", "maxResults", "mcp", + "Menlo", "metacharacters", "modelcontextprotocol", "msword", @@ -77,6 +84,7 @@ "myapp", "mypassword", "myproject", + "Neue", "Newtonsoft", "nextPageToken", "numstat", @@ -100,6 +108,7 @@ "sast", "sbom", "semver", + "Segoe", "shellcheck", "shortlog", "shortstat", @@ -136,13 +145,17 @@ "vulns", "WCAG", "webui", + "wordmark", "wordprocessingml", "worktree", "worktrees", "xoxb", "Zeroize", "zeroization", - "zeroized" + "zeroized", + "offbrand", + "onbrand", + "scriptable" ], "flagWords": [], "ignorePaths": [ diff --git a/.gitignore b/.gitignore index b1564d42..bf6465c6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,10 @@ Thumbs.db # Node node_modules pnpm-debug.log + +# Eval run outputs (scratch; regenerated by grade.py) +plugins/**/evals/runs/ + +# Python bytecode +__pycache__/ +*.pyc diff --git a/README.md b/README.md index 3597c415..5b2969c1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A curated collection of plugins for AI-assisted development at Bitwarden. Enable | [bitwarden-code-review](plugins/bitwarden-code-review/) | 1.11.0 | Autonomous code review agent following Bitwarden engineering standards with GitHub integration | | [bitwarden-delivery-tools](plugins/bitwarden-delivery-tools/) | 1.4.0 | Delivery lifecycle skills: initiative funnel navigation, work transitions, tech breakdowns and cross-team signoffs, commits, PRs, preflight, labeling | | [bitwarden-designer](plugins/bitwarden-designer/) | 0.1.0 | Product designer persona: Code of Conduct and 30/60/90 critique, critique facilitation; dispatches into bitwarden-design-tools | -| [bitwarden-design-tools](plugins/bitwarden-design-tools/) | 0.1.0 | Design toolkit: content style guide, Figma Dev Mode MCP, Bitwarden brand application, handoff prep, Design System governance, Product and Design Jira | +| [bitwarden-design-tools](plugins/bitwarden-design-tools/) | 0.2.0 | Design toolkit: content style guide, Figma Dev Mode MCP, Bitwarden brand application, handoff prep, Design System governance, Product and Design Jira | | [bitwarden-devops-engineer](plugins/bitwarden-devops-engineer/) | 0.1.3 | DevOps engineering assistant: workflow compliance linting, action security auditing, and org-wide CI/CD remediation | | [bitwarden-init](plugins/bitwarden-init/) | 1.2.0 | Initialize and enhance CLAUDE.md files with Bitwarden's standardized template format | | [bitwarden-product-analyst](plugins/bitwarden-product-analyst/) | 0.1.5 | Product analyst agent for creating comprehensive Bitwarden requirements documents from multiple sources | diff --git a/plugins/bitwarden-design-tools/.claude-plugin/plugin.json b/plugins/bitwarden-design-tools/.claude-plugin/plugin.json index 565bbf5e..013630ce 100644 --- a/plugins/bitwarden-design-tools/.claude-plugin/plugin.json +++ b/plugins/bitwarden-design-tools/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "bitwarden-design-tools", - "version": "0.1.0", + "version": "0.2.0", "description": "Design toolkit for Bitwarden — non-persona skills for the design lifecycle. Content style guide reference, Figma Dev Mode MCP usage, Bitwarden brand application, design-to-engineering handoff prep, Design System governance, and the Product and Design Jira workflow. Composed by the bitwarden-designer agent and usable standalone.", "author": { "name": "Bitwarden", @@ -18,6 +18,9 @@ "design-system", "brand", "branding", - "accessibility" + "accessibility", + "deliverable", + "html", + "dashboard" ] } diff --git a/plugins/bitwarden-design-tools/CHANGELOG.md b/plugins/bitwarden-design-tools/CHANGELOG.md index 6a9a18f9..3442341a 100644 --- a/plugins/bitwarden-design-tools/CHANGELOG.md +++ b/plugins/bitwarden-design-tools/CHANGELOG.md @@ -5,6 +5,29 @@ All notable changes to the `bitwarden-design-tools` plugin will be documented in The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2026-06-17 + +### Changed + +- `applying-bitwarden-branding` now covers both **building** on-brand standalone deliverables (dashboards, recaps, reports, slide decks, one-pagers, mockups) and **reviewing** whether a deliverable is on-brand. Reviews are calibrated to separate canonical violations from brand-silent pragmatic choices, and brand-silent choices that visibly shape the deliverable (voice and tone) are surfaced to the requester when working interactively rather than silently defaulted. The canon is now bundled (assets + tokens) for offline, verbatim use rather than referenced externally. +- `applying-bitwarden-branding` now ships **both light and dark surfaces by default**. Deliverables follow the device's `prefers-color-scheme` for the initial surface and expose a light/dark toggle that overrides it, with the dark surface derived from Deep Blue. Previously surface mode defaulted to light and was treated as a per-deliverable question. + +### Added + +- Bundled brand assets for `applying-bitwarden-branding`: `bitwarden-tokens.css` (palette + 36px radius + Inter on `:root`), the official lockup (`bitwarden-lockup-official.svg`), and derived per-variant SVGs (horizontal and vertical lockups, shield, wordmark, in blue and white), each with path data verbatim from the official lockup. +- Reference docs `typography.md` and `logo-usage.md`, and `examples/on-brand-one-pager.html` demonstrating light and dark compositions. +- `scripts/verify-brand-canon.sh` to detect drift between the bundled palette and the brand repository's `palette.scss` and report the correct values to use (drift detector; runs on demand). It never modifies the bundle. +- `evals/` regression harness for `applying-bitwarden-branding`: mock-only off-brand and on-brand fixtures, a pre-registered objective rubric, and a deterministic grader (`grade.py`) that reads the canonical palette and the official-logo signatures from the live bundled assets so it tracks canon changes. The grader covers the context-free checks (Inter, primary palette, verbatim lockup, 36px radius, off-brand-font and off-palette detection); the calibration dimensions (review specificity, false-positive/over-flagging) are left to a blind LLM grader. + +### Fixed + +- `--bw-yellow` corrected to `#FDC700` to match the brand repository's `$tertiary-yellow` (was `#FFD700`). +- Clarified the 36px radius rule: the foundation is for surfaces that float with space around them, and full-bleed headers/footers flush to the page or viewport edge should not be rounded (inset them so all corners float, or leave the flush edges square). Was producing rounded corners against straight page edges on full-bleed headers. + +### Removed + +- `references/brand-assets.md` — its repo-path asset inventory and trademark note are folded into `references/logo-usage.md`. + ## [0.1.0] - 2026-05-22 ### Added diff --git a/plugins/bitwarden-design-tools/README.md b/plugins/bitwarden-design-tools/README.md index f80384ef..ae500687 100644 --- a/plugins/bitwarden-design-tools/README.md +++ b/plugins/bitwarden-design-tools/README.md @@ -12,7 +12,7 @@ This plugin ships skills only — no agent. The persona half lives in [`bitwarde | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `content-style-guide` | Bitwarden's product content style guide for GUI copy — voice, tone, AP-style-with-exceptions grammar, sentence case in UI, no ampersands, and accessibility-first language at a U.S. 7th-grade reading level. Lean SKILL.md with detailed rules split across `references/grammar-mechanics.md` and `references/accessibility-rules.md`. | | `using-figma` | Read and inspect Figma designs via the Dev Mode MCP server. Per-job-to-be-done tool selection for read tools (`get_design_context`, `get_metadata`, `get_screenshot`, `get_variable_defs`, `search_design_system`, `get_libraries`), with Code Connect and write tools documented in their own subsections. Foundational — most other skills compose it. | -| `applying-bitwarden-branding` | Apply Bitwarden brand standards — logo usage, color palette, typography, iconography, and capitalization rules. Grounded in [bitwarden.com/brand](https://bitwarden.com/brand/) and the [bitwarden/brand](https://github.com/bitwarden/brand) repository. Full palette and asset inventory in `references/`. | +| `applying-bitwarden-branding` | Apply or review Bitwarden brand standards (palette, Inter, official lockup, 36px radius) on standalone deliverables and design-adjacent assets, with bundled canonical assets and tokens. Grounded in [bitwarden.com/brand](https://bitwarden.com/brand/) and the [bitwarden/brand](https://github.com/bitwarden/brand) repository. | | `preparing-design-handoff` | The end-of-In-Design gate / checklist. Confirm the Figma file is Ready-for-Dev (sections aligned to stories, tokens library-bound, strings annotated, edge states covered) and that the Jira state is aligned (Figma linked to the Epic's Design field, sections marked Ready for Dev, EM transitions the Epic). | | `evolving-design-system-components` | Propose a new UI pattern or modify an existing Component Library component per Bitwarden's published governance process — design-team alignment, Core vs. Recipe/Snowflake with UI Foundation, Figma branching and property conventions, review gates, merge timing. Figma conventions in `references/figma-conventions.md`. | | `navigating-design-jira-process` | Move design work through Bitwarden's Product and Design Jira workflow — final designs attached to tickets, the 30/60/90 critique cadence tracked in Figma, status transitions, and the one-off engineering story flow. | diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/SKILL.md b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/SKILL.md index a83fb679..87b85854 100644 --- a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/SKILL.md +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/SKILL.md @@ -1,137 +1,84 @@ --- name: applying-bitwarden-branding -description: Apply Bitwarden brand standards — logo usage, color palette, typography, iconography, and capitalization rules — grounded in bitwarden.com/brand and the bitwarden/brand repository. -when_to_use: Use when a task touches Bitwarden's visual brand surface in design work or design-adjacent assets. Triggers — "check the brand", "apply Bitwarden branding", "use the brand colors", "is this on-brand", "what color is Bitwarden blue", "what font does Bitwarden use", "logo usage", "brand guidelines", "brand assets", "shield". Not for product content voice or grammar (use `content-style-guide`). -allowed-tools: Skill +description: Apply or review Bitwarden branding on standalone shareable deliverables (dashboards, recaps, reports, slide decks, one-pagers, mockups) and design-adjacent assets. Use when building or auditing a Bitwarden-audience deliverable, on explicit asks like "make this look like Bitwarden", "Bitwarden-themed deck", "is this on-brand?", or "brand-check this", and proactively when producing a shareable Bitwarden deliverable with no other brand specified. Not for product UI in bitwarden/clients, the web vault, or mobile apps (use @bitwarden/components), third-party work, or partner co-branding. +allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/verify-brand-canon.sh:*) --- -# Applying Bitwarden Branding - -This skill grounds brand-application decisions in Bitwarden's two canonical sources: - -- **[bitwarden.com/brand](https://bitwarden.com/brand/)** — the brand guidelines hub, including - logo lockups, the radius system, social-post framing, product images, and B-roll. -- **[github.com/bitwarden/brand](https://github.com/bitwarden/brand)** — the source-of-truth - repository for SVG logos, PNG product icons (multiple sizes), the shield mark, color palette - in multiple formats (HEX, HSL, SCSS), screenshots, and media assets. - -When applying brand in real work, treat the repo as canonical for assets and the brand site as -canonical for usage rules. Detailed reference material lives in `references/brand-assets.md` -(asset inventory and file paths) and `references/color-palette.md` (full palette with usage -notes and SCSS variable names). - -## The five rules that catch the most mistakes - -1. **Capitalize the B in Bitwarden.** Always. The W is never capitalized. The only place - "bitwarden" appears lowercase is inside the official logo lockup — not in body copy, not in - headlines, not in handles or URLs. -2. **Primary palette before tertiary palette.** Green, Yellow, and Red are tertiary — use them - sparingly, primarily in product graphics and for success / warning / error communications. - They are not headline colors. -3. **Inter for everything.** Product and website. The available weights are 300 (light), 400 - (regular), 500 (medium), 600 (semi-bold), and 700 (bold). -4. **Logo safe-area is non-negotiable.** Horizontal lockup needs one Bitwarden-shield width of - clear space on every side. Vertical lockup uses the height of the "X" in the logotype. - Cramped logos break the lockup. -5. **36px radius system, but buttons are the exception.** Rounded corners follow a 36px radius - across primary brand surfaces. Buttons sit outside that system — don't apply 36px to - buttons. - -## Logo usage - -- **Default mark.** `/logos/logo-horizontal-blue.svg` from the brand repo. Horizontal is the - primary/preferred lockup. -- **Inverse (for dark backgrounds).** `/logos/logo-horizontal-white.svg`. -- **Vertical lockup.** Available for use cases where horizontal doesn't fit; the safe area uses - the height of the "X" in the logotype. -- **Product icon (the shield).** Available rounded and square, at 16/32/64/128/256 px PNG plus - SVG (`/logos/icon.svg`, `/logos/icon-inverse.svg`, `/icons/*.png`). The shield itself lives - at `/shield/`. -- **Product logos.** Unique lockups exist for individual Bitwarden products (Password Manager, - Secrets Manager, etc.). Use these "primarily for use in-product." - -Don't recolor, distort, rotate, or recompose the logo. If the supplied SVG doesn't fit the -need, reach out to the brand owners rather than improvising a variant. - -## Color palette quick reference - -Full palette with all variable names is in `references/color-palette.md`. Five colors carry -most of the work: - -| Color | HEX | Use | -| -------------- | --------- | ------------------------------------------------------ | -| Bitwarden Blue | `#175DDC` | Primary brand color. Headlines, primary CTAs, accents. | -| Deep Blue | `#0C3276` | Secondary brand color. Dense surfaces, headers. | -| Off White | `#F3F6F9` | Default light surface. | -| True Black | `#000000` | Default text on light surfaces. | -| Teal Highlight | `#2CDDE9` | Accent and highlight — pair with the blues. | - -Tertiary palette (Green `#7BF1A8`, Yellow `#FDC700`, Red `#FF6550`) is **sparingly** applied -for product graphics and success/warning/error states. The brand site is explicit on this. - -SCSS variable names (from the brand repo's `brand-colors/palette.scss`): - -- `$bitwarden-blue`, `$deep-blue`, `$off-white`, `$true-white`, `$true-black`, `$light-grey`, - `$teal-highlight`, `$light-teal-highlight`, `$tertiary-green`, `$tertiary-yellow`, - `$tertiary-red`. - -## Typography - -- **Typeface:** Inter (open-source, Google Fonts). -- **Weights available:** 300 (light), 400 (regular), 500 (medium), 600 (semi-bold), 700 (bold). -- **Use:** product UI and website body / headline copy. The brand site doesn't enumerate - weight-per-context rules; defer to product or marketing leads when an unusual case comes up. - -## Iconography - -- **Web icons.** Designed for a wide range of uses with more detail than the product icon. -- **Product icon (shield).** Available rounded and square at multiple sizes from the brand - repo's `/icons/` directory. - -Asset paths and full sizing tables are in `references/brand-assets.md`. - -## Capitalization and trademark - -- The "B" in **Bitwarden** is capitalized in all copy text. -- The "W" is never capitalized — neither `BITWARDEN` (in body copy) nor `bitWarden`. -- The only place "bitwarden" appears in lowercase is inside the official logo lockup itself. -- "Bitwarden" is a registered trademark of Bitwarden Inc. — surface this when content is - external-facing and trademark attribution is appropriate. - -## Composing with other skills - -- **`content-style-guide`.** Brand sits alongside content style. When reviewing user-visible - surfaces, walk both: this skill catches color, logo, and capitalization issues; the content - style guide catches voice, tone, sentence case, and accessibility. -- **`using-figma`.** Use `get_variable_defs` to check whether a design's colors are - library-bound and aligned to the brand palette; use `get_libraries` to confirm the right - design library is loaded before claiming a design is on-brand. -- **`preparing-design-handoff`.** Surface brand findings as part of the handoff gate — flag - them as Figma annotations or as open questions in the Epic when something is off-brand at - handoff time. Don't quietly fix. -- **`evolving-design-system-components`.** New patterns must respect the brand palette and the - 36px radius system (with the button exception). The Component Library governance review - catches obvious violations, but raise them explicitly when sponsoring a pattern. - -## Output format for brand checks - -When asked "is this on-brand?", structure the response as: - -1. **What's checked** — which brand surfaces this design touches (logo, color, typography, - iconography, capitalization). -2. **What's on-brand** — what's working and should stay. -3. **What's off-brand** — each finding tied to the specific brand rule it violates (cite the - section, e.g., "Tertiary palette overused — Green appears in three non-state surfaces, per - the bitwarden.com/brand tertiary-usage rule"). -4. **Proposed corrections** — concrete swaps the designer can apply, sourced from the canonical - palette or asset. - -Keep findings specific. "The headline uses `#7BF1A8` (tertiary green) where Bitwarden Blue -(`#175DDC`) is the brand-primary color" beats "the color is wrong." - -## Additional resources - -- **`references/brand-assets.md`** — full inventory of brand repo assets with file paths - (logos, icons, shield, screenshots, media assets). -- **`references/color-palette.md`** — full palette with HEX, HSL, SCSS variable names, and - per-color usage notes. +# Applying Bitwarden branding + +First, confirm the surface. If the target is product UI (`bitwarden/clients`, the web vault, or mobile apps), third-party work, or partner co-branding, stop: those use `@bitwarden/components` or another brand, not this skill. + +## Single source of truth + +[`bitwarden.com/brand`](https://bitwarden.com/brand) and its backing repository [`github.com/bitwarden/brand`](https://github.com/bitwarden/brand). Everything canonical here mirrors those; on any conflict, the brand repository's published values win. + +The canon is bundled in this skill (`assets/`, `references/`) so it is available offline and the logo can be embedded verbatim with no network round-trip. The bundle is the reliable default. Every file named in this skill (under `assets/`, `references/`, `examples/`, and `scripts/`) is present exactly as written, so reference it directly; there is no need to list a directory to confirm a file exists. + +Staying current is optional. Bundled values can fall behind the source over time. When network access is available, run the drift check first: + +``` +${CLAUDE_SKILL_DIR}/scripts/verify-brand-canon.sh +``` + +It fetches the authoritative palette from the brand repository and reports any drift. On a reported mismatch, use the correct value it prints when branding the deliverable; do not edit the bundled tokens in-session (refreshing them is a separate marketplace PR). On error or no network, fall back to the bundled canon. + +## The brand-canon checklist + +These are non-negotiable. They come straight from the brand repository. + +1. Use only the published palette: Bitwarden Blue (`#175DDC`), Deep Blue (`#0C3276`), Teal (`#2CDDE9`), Light Teal (`#A2F4FD`), the tertiary Green/Red/Yellow, and the neutral ramp (True White, Off White, Light Grey, Medium Grey, True Black). Invent no shades, tints, or alternate steps. See [`references/color-palette.md`](references/color-palette.md) for HEX/RGB/CMYK. +2. Use Inter for type, across headlines, copy, and text. See [`references/typography.md`](references/typography.md) for loading and fallback. +3. Use the official logo lockup. Horizontal is preferred; vertical and product-specific lockups exist for specific cases. Honor clear-space rules. Embed the bundled SVG verbatim; do not recreate the shield from scratch. See [`references/logo-usage.md`](references/logo-usage.md). +4. Apply the 36px rounded-radius foundation to container surfaces that float with space around them (panels, cards, hero sections, pills, badges). Do not round an edge that bleeds flush to the page or viewport boundary: a full-bleed header or footer should either be inset (given margin so all four corners float) or left square on the flush edges. Rounding only the bottom corners of an edge-to-edge band looks broken. Buttons are the only canonical exception. +5. Capitalize the B in Bitwarden, never the W. Lowercase "bitwarden" appears only inside the official logo lockup, never in copy, headlines, handles, or URLs. + +## Quick start (build) + +Three steps to put a deliverable on-brand: + +1. Link Inter. Add the Google Fonts preconnect and stylesheet to the document head. See [`references/typography.md`](references/typography.md) for the exact snippet. +2. Drop in the tokens. Paste the contents of [`assets/bitwarden-tokens.css`](assets/bitwarden-tokens.css) into a style block, or link to it. This provides the full palette as CSS custom properties (`--bw-blue`, `--bw-deep-blue`, and so on), the 36px radius, and Inter on `:root`. +3. Add the lockup. Pick the variant that matches the surface and available space; horizontal is preferred. Every variant ships verbatim from the official source under [`assets/`](assets/): + - Horizontal: `bitwarden-lockup-horizontal-blue.svg` (light) / `bitwarden-lockup-horizontal-white.svg` (dark) + - Vertical: `bitwarden-lockup-vertical-blue.svg` / `bitwarden-lockup-vertical-white.svg` + - Shield only (chip-scale): `bitwarden-shield-blue.svg` / `bitwarden-shield-white.svg` + - Wordmark only: `bitwarden-wordmark-blue.svg` / `bitwarden-wordmark-white.svg` + - Full official source: `bitwarden-lockup-official.svg` + + See [`references/logo-usage.md`](references/logo-usage.md) for the full catalog and when to use each. + +4. Ship light and dark. Default the surface to the device setting with `@media (prefers-color-scheme: dark)` and add a visible light/dark toggle that overrides it. Derive the dark surface from `--bw-deep-blue`; keep the palette and logo identical across modes. See "Surface mode" below and the bundled example. + +Steps 1-3 are the canonical surface; step 4 is the skill's standing default. Everything else is a pragmatic choice — see below. + +## Where the brand site is silent + +These decisions still have to be made, but the brand site does not prescribe them; they are pragmatic, not canonical. When working interactively and the choice will visibly shape the deliverable (voice and tone most of all), ask the requester rather than assume. Otherwise apply the default below and note the assumption in the deliverable. + +- Surface mode (light vs. dark). Ship both by default. Drive the initial surface from the device setting with `@media (prefers-color-scheme: dark)`, and include a visible control to toggle between light and dark that overrides the device default. Build the light surface from `--bw-off-white`/`--bw-true-white` with `--bw-deep-blue` text; derive the dark surface from `--bw-deep-blue` rather than inventing a new neutral. The palette and logo are identical in both modes — only the surface and text invert. The bundled example shows the pattern. +- Type scale (heading sizes, weights, line-heights). Pragmatic. A safe four-step default: display 2.5rem/700, section 1.5rem/600, body 1rem/400, caption 0.8125rem/500. See [`references/typography.md`](references/typography.md). +- Code font. Pragmatic. `"SF Mono", "JetBrains Mono", Menlo, Consolas, monospace`. The brand is silent on code typography. +- Component shapes (cards, banners, chips, toolbars, badges). Pragmatic. The brand defines no component vocabulary. Apply the 36px radius to container surfaces (canonical); choose whatever padding, spacing, and border treatment fits. +- Voice and tone. Pragmatic. Not in the brand site. Match the audience and channel. +- Accessibility and contrast specifics. Pragmatic. No published contrast matrix, so defer to WCAG AA. See the recommended pairings in [`references/color-palette.md`](references/color-palette.md). + +## Reviewing for brand compliance + +When asked whether something is on-brand, structure the answer as: + +1. What is checked: which brand surfaces the work touches (palette, typography, logo, radius, capitalization). +2. What is on-brand: what works and should stay. Affirm the correct things explicitly; do not only list problems. +3. What is off-brand: each finding tied to the specific canon rule it breaks, citing the actual value. "The headline uses `#46E08A` (off-palette green) where Bitwarden Blue `#175DDC` is the brand primary" beats "the color is wrong". +4. Proposed corrections: concrete swaps sourced from the canonical palette or bundled asset. + +Calibrate severity — this is where reviews go wrong. Separate canon violations from brand-silent choices: + +- Canon violation (flag it): off-palette color, non-Inter type, a recreated or altered logo, capitalizing the W in Bitwarden. These break the brand canon. +- Brand-silent choice (note as a judgment-call, never a hard failure): the existence of a dark surface mode, how strictly to apply the 36px radius to dense internal-tool components (the radius is canonical for primary brand surfaces; forcing it onto dense data cards is a judgment call), emoji vs. custom icons, a code-font choice, loading Inter from a CDN. The brand does not rule on these. Mention them as optional refinements, but do not mark them as failures or non-negotiable violations. Over-flagging defensible pragmatic choices erodes trust in the review. + +## References + +- [`references/color-palette.md`](references/color-palette.md): full palette (HEX/RGB/CMYK), WCAG-AA pairings, pragmatic surface guidance. +- [`references/typography.md`](references/typography.md): Inter loading and fallback, a pragmatic type scale, a pragmatic code-font stack. +- [`references/logo-usage.md`](references/logo-usage.md): lockup choice, clear-space rules, the official SVG URL, the do-not list. +- [`examples/on-brand-one-pager.html`](examples/on-brand-one-pager.html): a worked template to read and adapt — the canon (palette, Inter, shield, 36px radius) assembled in real markup across a light and a dark composition. The dark composition is one pragmatic option (background derived from `--bw-deep-blue`, surfaces lifted with a small `--bw-true-white` overlay, no invented neutral), not the prescribed one. Copy the structure, keep the canon, swap the pragmatics to fit. diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-horizontal-blue.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-horizontal-blue.svg new file mode 100644 index 00000000..36588a7d --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-horizontal-blue.svg @@ -0,0 +1,29 @@ + + + + Bitwarden + + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-horizontal-white.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-horizontal-white.svg new file mode 100644 index 00000000..949992bc --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-horizontal-white.svg @@ -0,0 +1,29 @@ + + + + Bitwarden + + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-official.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-official.svg new file mode 100644 index 00000000..845fb6f7 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-official.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-vertical-blue.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-vertical-blue.svg new file mode 100644 index 00000000..356c6500 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-vertical-blue.svg @@ -0,0 +1,29 @@ + + + + Bitwarden + + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-vertical-white.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-vertical-white.svg new file mode 100644 index 00000000..885265bd --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-lockup-vertical-white.svg @@ -0,0 +1,29 @@ + + + + Bitwarden + + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-shield-blue.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-shield-blue.svg new file mode 100644 index 00000000..61011bbf --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-shield-blue.svg @@ -0,0 +1,24 @@ + + + + Bitwarden + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-shield-white.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-shield-white.svg new file mode 100644 index 00000000..d8325026 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-shield-white.svg @@ -0,0 +1,24 @@ + + + + Bitwarden + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-tokens.css b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-tokens.css new file mode 100644 index 00000000..efc90386 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-tokens.css @@ -0,0 +1,66 @@ +/* + * Bitwarden brand tokens — canonical values only. + * + * Source of truth: https://bitwarden.com/brand + * + * What this file is: + * - The full published palette (HEX) as CSS custom properties. + * - The 36px rounded-radius foundation. + * - Inter as the primary font-family on :root. + * + * What this file is NOT: + * - A component vocabulary. There are no card/button/banner/chip styles here + * because they are not part of the Bitwarden brand. Component shape is a + * pragmatic choice the deliverable makes. + * - A theme. This file holds only canonical values, so it has no light/dark + * surface tokens — those are deliverable-level. The skill ships BOTH modes + * by default: drive the initial surface from the device setting + * (@media (prefers-color-scheme: dark)) with a light/dark toggle that + * overrides it, and derive the dark surface from Deep Blue (#0C3276) rather + * than inventing a new neutral. See the skill's example for the pattern. + * - A type scale. Heading sizes, weights, and line-heights are pragmatic + * choices the deliverable makes. + * + * The 36px radius applies to container surfaces (panels, cards, hero sections, + * pills, badges). Per brand canon, the ONLY exception is buttons. + */ + +:root { + /* Primary */ + --bw-blue: #175ddc; + --bw-deep-blue: #0c3276; + + /* Accents */ + --bw-teal: #2cdde9; + --bw-light-teal: #a2f4fd; + + /* Semantic — use sparingly, primarily for success/error communication */ + --bw-green: #7bf1a8; + --bw-red: #ff6550; + --bw-yellow: #fdc700; + + /* Neutrals */ + --bw-true-white: #ffffff; + --bw-off-white: #f3f6f9; + --bw-light-grey: #d8e2eb; + --bw-medium-grey: #99a7b5; + --bw-true-black: #000000; + + /* Radius foundation — 36px. Buttons are the only canonical exception. */ + --bw-radius: 36px; + --bw-radius-pill: 999px; + + /* Typography — Inter is the primary typeface per brand. */ + font-family: + "Inter", + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Oxygen, + Ubuntu, + Cantarell, + "Helvetica Neue", + Arial, + sans-serif; +} diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-wordmark-blue.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-wordmark-blue.svg new file mode 100644 index 00000000..02215ba7 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-wordmark-blue.svg @@ -0,0 +1,24 @@ + + + + Bitwarden + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-wordmark-white.svg b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-wordmark-white.svg new file mode 100644 index 00000000..1707e454 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/assets/bitwarden-wordmark-white.svg @@ -0,0 +1,24 @@ + + + + Bitwarden + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/README.md b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/README.md new file mode 100644 index 00000000..041116e5 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/README.md @@ -0,0 +1,79 @@ +# applying-bitwarden-branding — evals + +A small regression harness for the branding skill. It exercises both jobs the +skill does — **applying** brand to a deliverable and **reviewing** a deliverable +for brand compliance — against fixed mock subjects, and grades the outputs +deterministically where it can. + +## Fixtures are mock-only + +`inputs/` contains **only fabricated artifacts** — no real Bitwarden data, +metrics, or internal decks. Keep it that way. If a fixture needs to feel +realistic, invent the content. + +**Keep the fixtures free of eval-meta.** No comment or visible text should mark a +file as a fixture, call it on- or off-brand, label which choices are faults, or +point at the rubric. The skill under test reads the input verbatim, so any such +hint poisons the result — it would measure reading the answer key, not detecting +brand issues. Ground truth lives in this README and in `rubric.json`, which the +skill never sees. + +- `inputs/project-atlas-deck.html` — deliberately off-brand (Big Shoulders / IBM Plex + fonts, invented slate/green palette, no official lockup, sub-36px radii). +- `inputs/engineering-recap.html` — on-brand (Inter, Bitwarden palette, 36px + radius, official shield embedded, dark surface derived from Deep Blue via + `color-mix`, on-palette blue/teal chart series) with exactly one planted fault: + an off-palette orange CTA hover. The dark-mode choice itself is a brand-silent + judgment call a good review should accept, not flag. + +## Files + +| File | Purpose | +| ------------- | ---------------------------------------------------------------------------- | +| `evals.json` | The eval definitions: prompt, input files, expected output. | +| `rubric.json` | Pre-registered objective assertions and ground truth. | +| `grade.py` | Deterministic grader. Reads palette + logo signatures live from `../assets`. | +| `inputs/` | Mock subject artifacts. | + +## Running an eval + +1. For each eval in `evals.json`, run the skill against its `prompt` (with the + listed `files` available). For a baseline measurement, run the same prompt + with the skill disabled. +2. Save each run's deliverable under `runs//outputs/`: + - apply evals → the produced `.html` + - review evals → the review as `.md` +3. Grade: + ``` + ./grade.py # defaults to ./runs + ./grade.py /path/to/runs + ``` + It writes `grading.json` into each run dir and prints a summary table. + +`runs/` is scratch — do not commit it. + +## What is graded deterministically vs. not + +`grade.py` covers only the **context-free** checks, because those are the ones a +script does reliably: + +- **apply**: loads Inter, uses Bitwarden Blue / Deep Blue, embeds the official + lockup (not redrawn), applies a 36px radius, is valid self-contained HTML, and + — for the deck — drops the off-brand fonts. Also reports an off-palette hex + count (lower is better). +- **review (true-positive)**: detects the off-brand fonts, palette, missing + logo, and non-36px radii in the off-brand deck. + +The **judgment** dimensions are deliberately left to a blind LLM grader, because +they are exactly what a script gets wrong (we tried; it over-flagged sanctioned +Deep-Blue-derived dark ramps as violations): + +- **review specificity** (`cites_specific_values`): does the review cite actual + values/locations rather than generic advice? +- **review false-positives** (`affirms_correct_core`, `false_positive_count`): + on the on-brand control, does the review affirm the correct core, treat the + dark surface and data-viz series as judgment calls, and flag only the genuine + orange fault? Over-flagging defensible choices is the failure mode. + +Grade the judgment dimensions with independent, blind LLM passes (no access to +the expected-output text) and score against `rubric.json`'s ground truth. diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/evals.json b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/evals.json new file mode 100644 index 00000000..f554609f --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/evals.json @@ -0,0 +1,49 @@ +{ + "skill_name": "applying-bitwarden-branding", + "purpose": "Regression evals for the branding skill across both jobs it does: applying brand to a deliverable, and reviewing a deliverable for brand compliance. Run the skill (with-skill) against these subjects and grade the outputs; compare to a no-skill baseline when measuring the skill's contribution.", + "subjects": { + "deck": "inputs/project-atlas-deck.html — MOCK fictional decision deck, deliberately off-brand (Big Shoulders / IBM Plex fonts, invented slate/green palette, no official lockup, sub-36px radii). Contains no real data.", + "recap": "inputs/engineering-recap.html — MOCK fictional recap page, substantially on-brand (Inter, Bitwarden palette, 36px radius, official shield embedded) with brand-silent defensible choices (a Deep-Blue-derived dark surface, data-viz series) and one genuine minor fault (an off-palette orange accent). Contains no real data." + }, + "evals": [ + { + "id": 1, + "name": "apply-deck", + "task": "apply", + "prompt": "This is an internal decision deck (project-atlas-deck.html). I want to share it more widely at Bitwarden — make it look like a polished, on-brand Bitwarden deliverable. Update the file in place or save a branded copy.", + "files": ["inputs/project-atlas-deck.html"], + "expected_output": "A branded HTML deck: Inter typeface, Bitwarden palette (#175DDC primary, Deep Blue surfaces, Teal accent), the OFFICIAL Bitwarden lockup (not redrawn), 36px radius on container surfaces, no invented off-palette hexes. Slide structure preserved.", + "grades": ["grade.py:grade_apply"] + }, + { + "id": 2, + "name": "apply-from-scratch", + "task": "apply", + "prompt": "Generate a Bitwarden-themed HTML one-pager announcing a new engineering initiative to the wider company. Make it on-brand and shareable.", + "files": [], + "expected_output": "A from-scratch on-brand one-pager. Tests whether the skill fires, self-supplies canonical brand (palette / Inter / official lockup / 36px radius) rather than improvising, and references bundled assets rather than redrawing them.", + "grades": ["grade.py:grade_apply"] + }, + { + "id": 3, + "name": "review-tp-deck", + "task": "review", + "prompt": "Is this deck (project-atlas-deck.html) on-brand for Bitwarden? Walk me through exactly what's off and how to fix each thing.", + "files": ["inputs/project-atlas-deck.html"], + "expected_output": "A structured review detecting the real brand gaps: non-Inter fonts (Big Shoulders / IBM Plex), non-brand palette (slate/green, not Bitwarden blue/teal), missing official lockup, non-36px radii. Each finding tied to a specific brand rule, with concrete corrections.", + "grades": ["grade.py:grade_review_tp", "blind-llm:cites_specific_values"] + }, + { + "id": 4, + "name": "review-fp-recap", + "task": "review", + "prompt": "Is this page (engineering-recap.html) on-brand for Bitwarden? Flag anything that's off.", + "files": ["inputs/engineering-recap.html"], + "expected_output": "A review of an already-on-brand page. Should confirm the palette / Inter / lockup are correct, treat the Deep-Blue-derived dark surface and the data-viz series as brand-silent judgment calls (not failures), and flag the one genuine fault (the off-palette orange hover). Measures false-positive rate and over-flagging.", + "grades": [ + "blind-llm:affirms_correct_core", + "blind-llm:false_positive_count" + ] + } + ] +} diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/grade.py b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/grade.py new file mode 100755 index 00000000..0d07ddcb --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/grade.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +"""Deterministic, bias-free grader for the applying-bitwarden-branding evals. + +Mechanical checks only — no judgment. The subjective dimensions (review +specificity, false-positive count, affirming the correct core) are deliberately +NOT scored here; they are left for a blind LLM grader, because that calibration +is exactly the part a script cannot do (see the eval README). + +The canonical palette and the official-logo signatures are read from the skill's +LIVE bundled assets (../assets), not hardcoded, so the harness automatically +tracks canon and asset changes instead of drifting against a frozen copy. + +Usage: + grade.py [RUNS_DIR] # default: ./runs + +Expected layout of RUNS_DIR (one subdir per eval from evals.json): + RUNS_DIR//outputs/*.html (apply evals) + RUNS_DIR//outputs/*.md (review evals) + +Eval-name substrings drive which checks run: + "apply" -> grade_apply (HTML) "deck" also checks off-brand fonts + "review-tp" -> grade_review_tp (markdown, true-positive detection) + "review-fp" -> deferred to the blind LLM grader + +Writes grading.json into each run dir and prints a summary table. +""" +import json +import re +import sys +from pathlib import Path + +EVALS_DIR = Path(__file__).resolve().parent +ASSETS = EVALS_DIR.parent / "assets" +TOKENS = ASSETS / "bitwarden-tokens.css" +OFFBRAND_FONTS = ["big shoulders", "ibm plex"] + + +def norm_hex(h): + h = h.lower() + if len(h) == 4: # #abc -> #aabbcc + h = "#" + "".join(c * 2 for c in h[1:]) + return h + + +def load_canon(): + """Canonical palette from the live bundled tokens, plus short-form neutrals.""" + canon = {"#fff", "#000"} + if TOKENS.is_file(): + for m in re.finditer(r"#[0-9a-fA-F]{3,8}\b", TOKENS.read_text()): + canon.add(norm_hex(m.group(0)[:7])) + return canon + + +def load_lockup_sigs(): + """Whitespace-stripped path-data signatures from each bundled SVG asset. + Verbatim use of any official asset embeds one of these; a redrawn shield + will not. Truncated so minor reformatting in the output does not break the + match while staying long enough to be unique.""" + sigs = [] + for svg in sorted(ASSETS.glob("*.svg")): + ds = re.findall(r'\bd\s*=\s*"([^"]+)"', svg.read_text()) + if not ds: + continue + longest = max(ds, key=len) + stripped = re.sub(r"\s+", "", longest) + if len(stripped) >= 40: + sigs.append(stripped[:40]) + return sigs + + +CANON = load_canon() +LOCKUP_SIGS = load_lockup_sigs() + + +def strip_comments(html): + """Remove HTML and CSS comments so commentary never skews the checks.""" + html = re.sub(r"", " ", html, flags=re.DOTALL) + html = re.sub(r"/\*.*?\*/", " ", html, flags=re.DOTALL) + return html + + +def grade_apply(html, is_deck): + html = strip_comments(html) + low = html.lower() + nospace = re.sub(r"\s+", "", html) + hexes = [ + norm_hex(x) + for x in re.findall(r"#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{3}\b", html) + ] + off_palette = sorted(set(h for h in hexes if h not in CANON)) + radius36 = ( + bool(re.search(r"border-radius\s*:\s*[^;]*36px", low)) or "36px" in low + ) + checks = { + "loads_inter": bool( + re.search(r"family=inter\b", low) + or re.search(r"[\"']inter[\"']", low) + or re.search(r"font-family\s*:[^;{}]*\binter\b", low) + ), + "uses_blue_primary": "#175ddc" in low, + "uses_deep_blue": "#0c3276" in low, + "official_lockup_not_redrawn": any(s in nospace for s in LOCKUP_SIGS), + "radius_36": radius36, + "valid_self_contained_html": ( + "" in low and " 1 else EVALS_DIR / "runs" + if not runs.exists(): + print(f"No run outputs at {runs}. See README.md for the expected layout.") + return + if not CANON or len(CANON) < 5: + print("WARNING: canonical palette could not be read from", TOKENS) + rows = [] + for eval_dir in sorted(runs.iterdir()): + if not eval_dir.is_dir(): + continue + ev = eval_dir.name + out = eval_dir / "outputs" + files = list(out.glob("*")) if out.exists() else [] + html = next((f for f in files if f.suffix == ".html"), None) + md = next((f for f in files if f.suffix == ".md"), None) + rec = {"eval": ev, "produced": bool(files)} + if "apply" in ev and html: + c, m = grade_apply(html.read_text(errors="ignore"), is_deck="deck" in ev) + rec["checks"], rec["metrics"] = c, m + rec["pass_rate"] = round(sum(c.values()) / len(c), 2) + elif "review-tp" in ev and md: + c, m = grade_review_tp(md.read_text(errors="ignore")) + rec["checks"], rec["metrics"] = c, m + rec["pass_rate"] = round(sum(c.values()) / len(c), 2) + elif "review-fp" in ev and md: + rec["note"] = "false-positive scoring deferred to blind LLM grader" + (eval_dir / "grading.json").write_text(json.dumps(rec, indent=2)) + rows.append(rec) + print(f"{'eval':40} {'made':4} {'pass':5} detail") + for r in sorted(rows, key=lambda x: x["eval"]): + pr = r.get("pass_rate", "-") + fails = [k for k, v in r.get("checks", {}).items() if not v] + off_palette = r.get("metrics", {}).get("off_palette_hex_count") + ex = f" off_palette={off_palette}" if off_palette is not None else "" + note = f" ({r['note']})" if "note" in r else "" + print( + f"{r['eval']:40} {'y' if r['produced'] else 'N':4} " + f"{str(pr):5} fails={fails}{ex}{note}" + ) + + +if __name__ == "__main__": + main() diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/inputs/engineering-recap.html b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/inputs/engineering-recap.html new file mode 100644 index 00000000..a8628f5b --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/inputs/engineering-recap.html @@ -0,0 +1,187 @@ + + + + + + Engineering Weekly Recap — Sample + + + + + +
+ + Bitwarden + + + Bitwarden +
+
+

Engineering Weekly Recap

+

+ A fictional sample page. None of these numbers are real. +

+
+
+

Merged PRs

+
128
+
+
+

Median review time

+
6.4h
+
+
+

Deploys

+
42
+
+
+

+ Backend + Clients + Platform +

+ View the brand guide +
+ + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/inputs/project-atlas-deck.html b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/inputs/project-atlas-deck.html new file mode 100644 index 00000000..4d7ca831 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/inputs/project-atlas-deck.html @@ -0,0 +1,171 @@ + + + + + + Project Atlas — Service Reliability Review + + + + + +
+ +

Internal Decision Deck · Q3

+

Project Atlas: Service Reliability Review

+

+ A fictional internal deck. None of these numbers are real. +

+
+ +
+

Where we are

+

Reliability at a glance

+
+
+
99.2%
+

Availability (30d)

+

▼ 0.4 pts vs target

+
+
+
412ms
+

p95 latency

+

▲ improved 8%

+
+
+
7
+

Sev-2 incidents

+ needs attention +
+
+
+ +
+

What broke

+

Top incident themes

+
+
+

Connection pool exhaustion

+

Recurred across three services this quarter.

+ pool.max = 20 // too low +
+
+

Cache stampede

+

Thundering herd after a deploy-time flush.

+
+
+

Retry storms

+

Unbounded client retries amplified a brownout.

+
+
+
+ +
+

The ask

+

Proposed investments

+
    +
  • Adopt adaptive connection pooling across the platform.
  • +
  • Standardize a retry budget with jittered backoff.
  • +
  • Fund an on-call ergonomics pass for the reliability rotation.
  • +
+

Decision requested by end of quarter.

+
+ + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/rubric.json b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/rubric.json new file mode 100644 index 00000000..31999f10 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/evals/rubric.json @@ -0,0 +1,112 @@ +{ + "note": "Pre-registered BEFORE any blinded run exists. Assertions are objective and scriptable so grading is deterministic and bias-free. Provenance/drift is measured by OUTPUT INSPECTION, never by agent self-report (self-report changes behavior).", + "canonical_palette_lowercase": { + "blue": "#175ddc", + "deep_blue": "#0c3276", + "teal": "#2cdde9", + "light_teal": "#a2f4fd", + "green": "#7bf1a8", + "yellow": "#fdc700", + "red": "#ff6550", + "off_white": "#f3f6f9", + "true_white": "#ffffff", + "true_black": "#000000" + }, + "drift_watch": [ + "Yellow value disputed across sources (#fdc700 vs #ffd700). Resolve ground truth from bitwarden.com/brand at analysis time — this is a Phase-2 drift signal, not a gradable assertion yet." + ], + "official_lockup_signature": "Path-data substrings are extracted at grade time from the skill's live bundled assets (../assets/*.svg) by grade.py:load_lockup_sigs. Presence of any in an output distinguishes verbatim use of an OFFICIAL asset from a redrawn/improvised shield.", + "apply_assertions": [ + { + "id": "loads_inter", + "desc": "Output loads/uses Inter as the primary typeface", + "type": "scriptable" + }, + { + "id": "no_offbrand_fonts", + "desc": "No off-brand display/mono fonts (Big Shoulders, IBM Plex) remain as primary type", + "type": "scriptable", + "applies_to": ["eval1_deck"] + }, + { + "id": "uses_blue_primary", + "desc": "Bitwarden Blue #175ddc present and used as a primary accent", + "type": "scriptable" + }, + { + "id": "uses_deep_blue", + "desc": "Deep Blue #0c3276 present", + "type": "scriptable" + }, + { + "id": "palette_purity", + "desc": "Count of distinct hex colors OUTSIDE the canonical palette (lower is better); report count, not just pass/fail", + "type": "scriptable_metric" + }, + { + "id": "official_lockup_not_redrawn", + "desc": "Output embeds the official lockup path signature rather than a hand-drawn shield", + "type": "scriptable" + }, + { + "id": "radius_36", + "desc": "border-radius:36px applied to at least one container surface", + "type": "scriptable" + }, + { + "id": "valid_self_contained_html", + "desc": "Parses as a complete standalone HTML document", + "type": "scriptable" + } + ], + "review_tp_assertions": { + "ground_truth_faults_in_deck": [ + "non-Inter fonts (Big Shoulders Display, IBM Plex Sans/Mono)", + "off-brand palette (slate/green/amber; none of the canonical hexes present)", + "missing official Bitwarden lockup", + "container radii far below 36px" + ], + "assertions": [ + { + "id": "detect_fonts", + "desc": "Flags the non-Inter typography", + "type": "scriptable_keyword" + }, + { + "id": "detect_palette", + "desc": "Flags the off-brand palette / absence of canonical hexes", + "type": "scriptable_keyword" + }, + { + "id": "detect_missing_logo", + "desc": "Flags the missing official lockup", + "type": "scriptable_keyword" + }, + { + "id": "detect_radius", + "desc": "Flags the non-36px radii", + "type": "scriptable_keyword" + }, + { + "id": "cites_specific_values", + "desc": "Cites actual values/line refs from the file (not generic advice)", + "type": "blind_llm" + } + ] + }, + "review_fp_assertions": { + "note": "The mock control (inputs/engineering-recap.html) is on-brand except for one planted fault: the off-palette orange CTA hover (#ff7a18). Everything else is canon-compliant — the dark surface is derived from Deep Blue via color-mix (not an invented neutral) and the chart series use only on-palette blue/teal tokens. The one brand-silent judgment call is the dark surface MODE itself, which a good review should accept rather than flag. A false positive is any hard finding other than the orange (for example flagging the dark mode, the Deep-Blue-derived surface, or the on-palette series).", + "assertions": [ + { + "id": "affirms_correct_core", + "desc": "Confirms Inter + core palette are correct rather than flagging them", + "type": "blind_llm" + }, + { + "id": "false_positive_count", + "desc": "Count of findings that are NOT genuine faults (lower is better)", + "type": "blind_llm_metric" + } + ] + } +} diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/examples/on-brand-one-pager.html b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/examples/on-brand-one-pager.html new file mode 100644 index 00000000..6440c9f6 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/examples/on-brand-one-pager.html @@ -0,0 +1,531 @@ + + + + + + + On-brand one-pager — Bitwarden + + + + + + + + + +
+
+ + + Bitwarden + + + Bitwarden + + +
+ +
+ On-brand example +

The brand canon, applied.

+

+ Palette, Inter, the official shield, and the 36px radius foundation — + assembled into one valid composition. The surface follows your device + by default; use the header toggle to switch light and dark. The + canonical pieces are identical in both. +

+
+ +
+
+
Canonical pieces applied
+
4
+
+
+
Pragmatic choices made
+
3
+
+
+ +
+

What this page demonstrates

+

+ The hero, stat tiles, and body card all carry the 36px radius + foundation. The eyebrow chip and the theme toggle use the radius-pill + token. Buttons (the toggle is one) are the only canonical exception to + the 36px system — here, the toggle uses the pill radius rather than + the foundation radius. +

+

+ The shield in the header is the bundled inline asset, sized for + chip-scale use. For a larger lockup or anything shareable externally, + fetch the official horizontal lockup from bitwarden.com/brand instead + of scaling the shield up. +

+

+ Both surface modes ship by default: the page follows the device's + light/dark preference and the header toggle overrides it. The brand + site does not prescribe surface mode, so the values are pragmatic — + the dark surface derives its background from + --bw-deep-blue and lifts elevated panels with a 6% + --bw-true-white overlay, with no new neutral introduced. +

+
+ + +
+ + + + diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/brand-assets.md b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/brand-assets.md deleted file mode 100644 index 0eec090c..00000000 --- a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/brand-assets.md +++ /dev/null @@ -1,72 +0,0 @@ -# Bitwarden Brand Asset Inventory - -The canonical source is the [bitwarden/brand](https://github.com/bitwarden/brand) GitHub -repository. Use raw GitHub URLs (`https://github.com/bitwarden/brand/raw/main/`) or clone -the repo when assets are needed locally. Paths below are repo-relative. - -## Logos - -| Asset | Path | Notes | -| ---------------------------- | ---------------------------------- | ------------------------------------------- | -| Default horizontal (primary) | `/logos/logo-horizontal-blue.svg` | Use on light backgrounds. Preferred lockup. | -| Inverse horizontal | `/logos/logo-horizontal-white.svg` | Use on dark backgrounds. | -| Vertical lockup | `/logos/` (see repo) | Use when horizontal won't fit. | -| Product icon (default, SVG) | `/logos/icon.svg` | The Bitwarden shield mark. | -| Product icon (inverse, SVG) | `/logos/icon-inverse.svg` | For dark contexts. | - -**Safe area:** horizontal lockup needs one Bitwarden-shield width of clear space on every -side. Vertical lockup uses the height of the "X" in the logotype. - -**Don't:** recolor, distort, rotate, stretch, recompose, or add effects (shadow, gradient, -outline) to the logo. If a unique need arises, escalate to the brand owners. - -## Product icon (shield) — PNG sizes - -Both rounded and square variants. The shield is at `/shield/` for the bare-mark version -without rounding or square framing. - -| Variant | Sizes available | Path prefix | -| ------- | ----------------------- | --------------------------------- | -| Rounded | 16, 32, 64, 128, 256 px | `/icons/x.png` | -| Square | 16, 32, 64, 128, 256 px | `/icons/square-x.png` | - -For sizes beyond the table, check `/icons/` in the repo for additional pre-rendered sizes. - -## Product logos - -Unique lockups exist for individual Bitwarden products (Password Manager, Secrets Manager, -etc.) and are described on [bitwarden.com/brand](https://bitwarden.com/brand/) as "primarily -for use in-product." Source these from the brand site directly rather than reconstructing -them. - -## Screenshots and media - -| Asset | Path | -| ----------------- | -------------------------------------------- | -| Product app combo | `/screenshots/apps-combo.png` | -| Full screenshots | `/screenshots/` (see repo for all platforms) | -| Media assets | `/media-assets/` (additional media) | - -The brand site links to Google Drive-hosted Password Manager and Secrets Manager product -images and Vimeo-hosted B-roll footage available for "public use and distribution." - -## Source files - -The `/src/` directory holds source files for the brand assets (typically Illustrator or -similar). Treat these as designer-only — use the exported SVG/PNG in production work. - -## When the asset you need isn't here - -- **For a missing format:** check the brand site first (`bitwarden.com/brand`); some assets - live there but not in the repo. -- **For a missing size:** open an issue on the brand repo with the use case and the requested - size, rather than scaling up an existing PNG (rounded corners and shield proportions don't - scale cleanly). -- **For a missing color variant:** don't generate one. The blue and the inverse-white are the - sanctioned variants. Surface the gap to the brand owners. - -## Trademark - -"Bitwarden" is a registered trademark of Bitwarden Inc. Apply the ® mark or trademark -attribution per the destination surface's standards (typically first reference per page in -external-facing content; not required in product UI). diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/color-palette.md b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/color-palette.md index 7dffcd38..5ec98b2c 100644 --- a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/color-palette.md +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/color-palette.md @@ -30,6 +30,26 @@ colors. | Tertiary Yellow | `#FDC700` | `253, 199, 0` | `hsla(47, 100%, 50%, 1)` | `$tertiary-yellow` | Warning states | | Tertiary Red | `#FF6550` | `255, 101, 80` | `hsla(5, 100%, 66%, 1)` | `$tertiary-red` | Error / destructive states | +## Token names + +The bundled `assets/bitwarden-tokens.css` exposes the palette as CSS custom properties, mapped +from the SCSS source. Cite whichever namespace fits the context. + +| SCSS (`palette.scss`) | CSS custom property | +| -------------------------- | ------------------- | +| `$bitwarden-blue` | `--bw-blue` | +| `$deep-blue` | `--bw-deep-blue` | +| `$teal-highlight` | `--bw-teal` | +| `$light-teal-highlight` | `--bw-light-teal` | +| `$tertiary-green` | `--bw-green` | +| `$tertiary-yellow` | `--bw-yellow` | +| `$tertiary-red` | `--bw-red` | +| `$off-white` | `--bw-off-white` | +| `$true-white` | `--bw-true-white` | +| `$true-black` | `--bw-true-black` | +| `$light-grey` | `--bw-light-grey` | +| (Medium Grey, no SCSS var) | `--bw-medium-grey` | + ## CMYK (for print) The brand site lists CMYK alongside HEX/RGB for use in print and produced materials. Pull from @@ -47,7 +67,7 @@ the brand site directly when print specs are needed — these are not in the SCS | Teal Highlight | 58, 0, 15, 0 | | Light Teal | 30, 0, 5, 0 | | Tertiary Green | 49, 0, 30, 5 | -| Tertiary Yellow | 0, 16, 100, 0 | +| Tertiary Yellow | 0, 21, 100, 1 | | Tertiary Red | 0, 60, 69, 0 | ## Application rules @@ -59,6 +79,39 @@ the brand site directly when print specs are needed — these are not in the SCS - **Tertiary palette is restrained.** Two or three tertiary swatches on one screen is usually too many. State semantics (success/warning/error) are the right use; decorative use is not. +## Recommended pairings (WCAG AA, body-text scale) + +Pairings labeled "AA" hit at least 4.5:1 for normal text. Pairings labeled "AA-large" hit at +least 3:1 — use only for ≥18pt or ≥14pt bold. + +| Foreground | Background | Status | Notes | +| ----------------- | ----------------- | -------- | ---------------------------------------------------------- | +| `--bw-true-black` | `--bw-true-white` | AA | Default body text on light surfaces. | +| `--bw-deep-blue` | `--bw-true-white` | AA | Body or heading text on light surfaces. | +| `--bw-deep-blue` | `--bw-off-white` | AA | Body text on a slightly softened light surface. | +| `--bw-blue` | `--bw-true-white` | AA-large | Headings or CTAs, not body. Body Blue-on-white is too low. | +| `--bw-true-white` | `--bw-deep-blue` | AA | Default body text on dark surfaces. | +| `--bw-true-white` | `--bw-blue` | AA-large | Headings or CTAs, not body. | +| `--bw-deep-blue` | `--bw-light-teal` | AA | Accent panel with readable body. | + +> The brand site does not publish a contrast matrix. The pairings above are pragmatic — verify +> against the actual size, weight, and rendering of the deliverable using a contrast checker +> (e.g. WebAIM). + +## Surface choices (pragmatic — the brand site is silent) + +The brand site does not prescribe a surface mode for deliverables. Two safe defaults: + +- **Light surface** — use `--bw-off-white` or `--bw-true-white` as the page background, + `--bw-deep-blue` for text, `--bw-blue` and `--bw-teal` for accents. +- **Dark surface** — derive the page background from `--bw-deep-blue` (use it directly, or + compose a slightly lighter shade on top via a 10-20% opacity `--bw-true-white` layer). **Do + not invent a new dark neutral.** Use `--bw-true-white` for body text and `--bw-teal` / + `--bw-light-teal` for accents. + +Whichever surface is chosen, document the decision in the deliverable so a reader knows it is a +deliverable-level call, not brand canon. + ## Common off-brand patterns to catch - **Off-system blues.** Anything that isn't `#175DDC` or `#0C3276` claiming to be "the @@ -74,5 +127,7 @@ the brand site directly when print specs are needed — these are not in the SCS Treat the brand site and brand repo as authoritative. If a color appears in product code but not on this page, check `brand-colors/palette.scss` in the repo first — that file is the -canonical SCSS source and is what this reference mirrors. If the palette has drifted, update -this file in a separate PR. +canonical SCSS source and is what this reference mirrors. Run +`scripts/verify-brand-canon.sh` to compare the bundled tokens against that source at any time; +on drift it prints the correct values to use in the deliverable. Updating this file (and +`assets/bitwarden-tokens.css`) to match is a separate PR. diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/logo-usage.md b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/logo-usage.md new file mode 100644 index 00000000..0edb15f4 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/logo-usage.md @@ -0,0 +1,79 @@ +# Logo usage + +Source of truth: [`bitwarden.com/brand`](https://bitwarden.com/brand). Lockup choice, clear-space rules, and the official SVG come from there. + +## Lockups + +- **Horizontal lockup — primary.** Per the brand site: _"The primary and preferred composition will be the horizontal lockup."_ Use this unless the available space makes it impossible. +- **Vertical lockup — secondary.** Use only when horizontal won't fit — e.g. narrow sidebars, vertical banners. +- **Product-specific lockups.** Bitwarden products have their own lockups for in-product UI and comparative diagrams. Use these when the artifact is specifically about a single product (e.g. Password Manager, Secrets Manager). These lockups are NOT bundled with this plugin — fetch them from `bitwarden.com/brand`. + +## Color variants + +The brand site ships two color variants of every lockup: + +- **Brand blue (`#175DDC`)** — for use on light surfaces. +- **White (`#FFFFFF`)** — for use on dark surfaces. + +Switching between blue and white on the appropriate surface is **not** a recolor. Both ship in the official lockup file. Using blue on a dark surface, or recoloring to any other shade, is. + +## Clear space + +- **Horizontal logo** — at minimum, leave one shield-width of clear space on every side. Nothing — text, icons, illustration, edge of canvas — encroaches inside that margin. +- **Vertical logo** — at minimum, leave one X-height (the height of the lowercase "x" in the "Bitwarden" wordmark) of clear space on every side. + +## Bundled assets + +This plugin ships the canonical paths extracted verbatim from the official lockup SVG. Use whichever variant fits the deliverable. + +| File | What it is | Use on | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| `assets/bitwarden-lockup-official.svg` | The full official lockup file, verbatim from the brand site. Contains every variant at every scale, both colors, on one canvas. | Reference. Use when you need the canonical source file directly. | +| `assets/bitwarden-lockup-horizontal-blue.svg` | Primary horizontal lockup (shield + wordmark) | Light surface | +| `assets/bitwarden-lockup-horizontal-white.svg` | Primary horizontal lockup (shield + wordmark) | Dark surface | +| `assets/bitwarden-lockup-vertical-blue.svg` | Secondary vertical lockup (shield above wordmark) | Light surface | +| `assets/bitwarden-lockup-vertical-white.svg` | Secondary vertical lockup (shield above wordmark) | Dark surface | +| `assets/bitwarden-shield-blue.svg` | Shield only | Light surface | +| `assets/bitwarden-shield-white.svg` | Shield only | Dark surface | +| `assets/bitwarden-wordmark-blue.svg` | Wordmark only | Light surface | +| `assets/bitwarden-wordmark-white.svg` | Wordmark only | Dark surface | + +Every derived asset preserves the original path data and coordinate system from the official lockup; only the surrounding `` wrapper and the `viewBox` crop are local. The shield is the same path at every size — scale it via the consuming page's CSS rather than swapping to a different shield variant. + +### When to use which + +- **Default to a full lockup** (horizontal preferred, vertical only when horizontal doesn't fit). +- **Use the shield alone** for small chip-scale uses where a full lockup would be illegible — e.g. a 32px header chip, a favicon-adjacent context. +- **Use the wordmark alone** only when the shield is already established elsewhere on the same deliverable (e.g. a long-form document where the header carries the shield and a footer carries the wordmark). The brand site does not prescribe a wordmark-only lockup; this is a pragmatic affordance. + +### Tinting from a single asset + +If you want a single shield/wordmark asset to flow with the deliverable's surface (light or dark) via CSS, swap the SVG's `fill="#175DDC"` for `fill="currentColor"` in your local copy and set the surrounding element's `color` property. Switching between blue and white this way is canonical; setting `color` to any other value is a recolor and not permitted. + +## Do-not list + +- **Don't recolor** to anything other than the published blue or white. +- **Don't outline** the logo or wordmark. +- **Don't compress or stretch.** Preserve the aspect ratio. +- **Don't place on busy backgrounds** without a solid panel underneath. The shield must read cleanly. +- **Don't recreate from scratch** when the official SVG is available — your trace will not match the canonical outlines. +- **Don't rotate** or apply effects (drop shadow, glow, gradient overlay). + +## Non-bundled assets and source + +This plugin bundles the lockups, shield, and wordmark above. For anything else, the canonical source is the [bitwarden/brand](https://github.com/bitwarden/brand) repository (raw URLs: `https://github.com/bitwarden/brand/raw/main/`) or [bitwarden.com/brand](https://bitwarden.com/brand): + +- **Product-specific lockups** (Password Manager, Secrets Manager, etc.) — for in-product UI and comparative diagrams; not bundled. +- **Product icon, PNG** — rounded `/icons/x.png` and square `/icons/square-x.png` at 16, 32, 64, 128, 256 px. The bare shield mark is at `/shield/`; the vector shield is `/logos/icon.svg` (`/logos/icon-inverse.svg` for dark contexts). + +For a missing size, request it on the brand repo rather than scaling a PNG (rounded corners and shield proportions do not scale cleanly). For a missing color variant, do not generate one. Blue and inverse-white are the only sanctioned variants, so surface the gap to the brand owners. + +The bundled lockups were extracted verbatim from the official source file: + +``` +https://images.ctfassets.net/7rncvj1f8mw7/6RNJEeiUeUvaJFcUXV5R49/e73a356cad20bce2336afcff8829485b/BitwardenLogo.svg +``` + +## Trademark + +"Bitwarden" is a registered trademark of Bitwarden Inc. Apply the ® mark or trademark attribution per the destination surface's standards (typically the first reference per page in external-facing content; not required in product UI). diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/typography.md b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/typography.md new file mode 100644 index 00000000..b96766da --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/references/typography.md @@ -0,0 +1,67 @@ +# Typography + +Source of truth: [`bitwarden.com/brand`](https://bitwarden.com/brand). The brand site says: _"Inter is an open-source Google font. It is used for all headlines, copy and text."_ + +## Primary typeface — Inter (canonical) + +Use Inter for headlines, body copy, captions — everything except code. + +### Loading Inter from Google Fonts + +For a one-pager or dashboard that can hit Google's CDN, link Inter with a `preconnect` for performance: + +```html + + + +``` + +If the deliverable will be opened offline or in an environment that blocks Google Fonts, ship Inter as a self-hosted woff2 — but verify you're using the file released under the SIL Open Font License so redistribution is legitimate. + +### Fallback stack + +When Inter fails to load, fall back to system sans-serif before the generic. The tokens CSS already sets this on `:root`: + +```css +font-family: + "Inter", + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + Oxygen, + Ubuntu, + Cantarell, + "Helvetica Neue", + Arial, + sans-serif; +``` + +## Type scale (pragmatic — the brand site is silent) + +The brand site doesn't prescribe heading sizes, weights, or line-heights. Anything you choose is a deliverable-level decision, not brand canon. + +Reasonable defaults for an HTML one-pager or dashboard: + +| Role | Size | Weight | Line-height | +| --------------- | ---------------- | ------ | ----------- | +| Display heading | 2.5rem (40px) | 700 | 1.15 | +| Section heading | 1.5rem (24px) | 600 | 1.25 | +| Body | 1rem (16px) | 400 | 1.55 | +| Caption / label | 0.8125rem (13px) | 500 | 1.4 | + +If your deliverable has a stronger existing scale (e.g. a slide template), keep that — it's the right level for that artifact to decide. + +## Code typography (pragmatic — the brand site is silent) + +The brand site does not address code typography. A reasonable default monospace stack: + +```css +font-family: + "SF Mono", "JetBrains Mono", Menlo, Consolas, "Liberation Mono", monospace; +``` + +Avoid pairing code in a heavy slab — code should sit visually quieter than body text, not louder. diff --git a/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/scripts/verify-brand-canon.sh b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/scripts/verify-brand-canon.sh new file mode 100755 index 00000000..a09f2606 --- /dev/null +++ b/plugins/bitwarden-design-tools/skills/applying-bitwarden-branding/scripts/verify-brand-canon.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# +# verify-brand-canon.sh +# +# Drift detector for the bundled Bitwarden palette tokens. Compares the bundled +# values in assets/bitwarden-tokens.css against the authoritative source, +# bitwarden/brand (brand-colors/palette.scss), and reports any drift along with +# the correct live value to use. +# +# This script never modifies the bundle. If it reports drift, use the printed +# "correct" values in the deliverable being branded. Refreshing the bundled +# tokens themselves is a separate change to the bitwarden-design-tools plugin +# (a marketplace PR), not something to do mid-session. +# +# Run it before building a deliverable when network is available. +# +# Exit codes: +# 0 bundle matches the source +# 1 drift detected (correct values printed) +# 2 could not fetch the source (offline / upstream moved); bundle untouched +# +# Requires: curl. + +set -euo pipefail + +SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TOKENS="$SKILL_DIR/assets/bitwarden-tokens.css" +SRC_URL="https://api.github.com/repos/bitwarden/brand/contents/brand-colors/palette.scss" + +# palette.scss variable name : tokens.css custom property +MAP=" +bitwarden-blue:--bw-blue +deep-blue:--bw-deep-blue +teal-highlight:--bw-teal +light-teal-highlight:--bw-light-teal +tertiary-green:--bw-green +tertiary-yellow:--bw-yellow +tertiary-red:--bw-red +" + +if [ ! -f "$TOKENS" ]; then + echo "ERROR: bundled tokens not found at $TOKENS" >&2 + exit 2 +fi + +echo "Checking bundled brand tokens against bitwarden/brand ..." +SCSS="$(curl -fsSL -H 'Accept: application/vnd.github.raw' "$SRC_URL")" || { + echo "ERROR: could not fetch palette.scss (offline or upstream moved). Bundle unchanged." >&2 + exit 2 +} + +drift=0 +while IFS= read -r pair; do + [ -z "$pair" ] && continue + src_name="${pair%%:*}" + var="${pair##*:}" + live="$(printf '%s\n' "$SCSS" | grep -iE "\\\$${src_name}[[:space:]]*:" | grep -oiE '#[0-9a-f]{6}' | head -1 | tr 'A-F' 'a-f' || true)" + have="$(grep -iE "^[[:space:]]*${var}[[:space:]]*:" "$TOKENS" | grep -oiE '#[0-9a-f]{6}' | head -1 | tr 'A-F' 'a-f' || true)" + + if [ -z "$live" ]; then + echo "WARN: \$${src_name} not found upstream (palette schema may have changed)" + drift=1 + continue + fi + if [ "$live" != "$have" ]; then + printf 'DRIFT %-16s bundled %-9s correct %-9s ($%s)\n' "$var" "${have:-none}" "$live" "$src_name" + drift=1 + fi +done <&2 + echo "Bundled brand tokens are out of date. Use the 'correct' values above in" >&2 + echo "the deliverable you are branding. Do not edit the bundle here: refreshing" >&2 + echo "assets/bitwarden-tokens.css is a separate change to the bitwarden-design-tools" >&2 + echo "plugin (open a marketplace PR)." >&2 + exit 1 +fi + +echo "OK: bundled palette matches bitwarden/brand."