Skip to content

About

okens, base styles, and components for building on-brand CSA pages, apps, and prototypes. Every class below is prefixed csa- so it never collides with cloudsecurityalliance.org's own CSS.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

43 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CSA Component Library

Tokens, base styles, and components for building on-brand Cloud Security Alliance pages, apps, and prototypes. This library is the source of truth for how CSA things look — csa-component-library-SPEC.md was the original spec it was built from, and a handful of early CSA program pages supplied real markup for nav, carousel, and badges during that initial build. Those pages have kept evolving independently since; this library doesn't track them going forward — it's the canonical version now, not a mirror of theirs.

Using it

  • Starting a new page: copy dist/csa-library.html and the dist/assets/ folder next to it — it's a complete, minimal HTML document with every token and component already inlined in a <style> block, the two required font <link> tags in <head>, and placeholder nav/footer/<main> you fill in. Build your content with the classes documented in the showcase, and pull logos/icons/illustrations from assets/ by relative path (e.g. assets/icons/ai-security.svg).

  • Font loading — always the two <link> tags, never inlined: any page using this library (whether copied from dist/csa-library.html as-is, or built by pulling these tokens/components into an existing site) must keep these as live <link> tags in its own <head>:

    <link rel="stylesheet" href="https://use.typekit.net/tsm2vln.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Red+Hat+Text:wght@400;500;600;700&display=swap">

    Don't copy/inline the kit's underlying @font-face CSS into your own stylesheet instead — Adobe's kit is domain-restricted and can change its served CSS/font URLs over time, so a live <link> stays correct automatically and a pasted snapshot can silently stop working. If the <link> is missing entirely, --font/--font-kicker (typography.css) just fall back to the next font in their stack with no error, so it's easy to miss without checking.

  • Browsing what's available: open src/docs/index.html in a browser. It's the live component showcase — palette swatches, type scale, all 3 surfaces, every button variant, card-glass, badges, nav, carousel, tabs, and real-layout examples built entirely with the library's own classes. It links to the src/ files directly (not dist/), so editing any component CSS shows up on refresh with no build step.

  • Rebuilding dist/csa-library.html after changing anything in src/:

    node build.mjs

Styling only, not behavior

This repo defines how CSA things look; each application defines how they actually work. CSS is portable across static pages, Rails, React, etc., and stays within the visual layer — JS behavior is tied to the state and conventions of whatever app it lives in, so this library ships no JS. dist/csa-library.html has zero <script> tags.

Most components are pure CSS already (hover states, transitions, the card-reveal grow animation). The few that used to have a companion .js file — Tabs, Carousel, Tags' filter-toggle/removable behavior — still document their intended interaction, just as a BEHAVIOR SPEC comment at the top of the component's .css file instead of working code: the markup contract, the expected click/keyboard behavior, and a link to the relevant WAI-ARIA pattern where one applies. Implement that contract in whatever fits the consuming app's own stack. This also happens to work well with LLM-assisted development — the spec tells the model what a component is supposed to do, and it can generate an implementation that actually fits the framework it's landing in, rather than forcing this repo's one-size-fits-all version.

The showcase (src/docs/index.html) is the one exception: it runs small demo-only harnesses for Tabs/Carousel/Tags purely so the page is interactive to click through. That script is clearly marked demo-only, lives only in the docs page, and build.mjs never bundles it into dist/.

Project structure

src/
  tokens/       colors, typography, spacing, shadows — design tokens only
  base/         reset.css — bare-element resets (*, html, body, a, section);
                icon-glyph.css — shared .csa-icon-glyph mask primitive used
                by tags.css and badges.css
  components/   one CSS file per component — no shipped JS, see
                "Styling only, not behavior" above
  assets/       real brand assets that ship with the library — see below
  docs/         index.html (the showcase) + docs/assets/ (demo-only fixtures,
                not shipped — logos/photos used to illustrate components)
build.mjs       concatenates src/ into dist/csa-library.html,
                copies src/assets/ -> dist/assets/
dist/           csa-library.html + assets/ — together, what teammates copy

Brand assets (src/assets/)

Real logos/icons/illustrations, kept separate from src/docs/assets/ (which is scoped to showcase-only demo fixtures, not shipped). Copied into every asset zip as-is — filenames unchanged from the source so they stay traceable back to the original design files.

Folder Count Format Naming
logos/ 164 PNG Mixed — CSA certification/program logos ({Program}-logo-{CMYK|RGB|grayscale|white}.png) plus ~52 one-off badges (Credly, membership, STAR, council)
icons/ 840 (726 SVG + 114 PNG) mostly SVG 120 unique icons × 6 color variants each: base, -CSA-blue, -light-blue, -red-orange, -white, -yellow-orange. The 19 most recently added icons (AI-Automation, Click, Clock, Connect, Delete, Document-Locked, Export, Folder-Locked, Gate, Info, Laptop-Locked, Phone-Locked, Safe, Scan-LLM, Strategy, Support, Target, Window, Window-Locked) also ship a PNG alongside each SVG variant; the original 101 remain SVG-only
illustrations/ 182 PNG ~33 scenes × 5 variants each: - dark, - light, - white bg, - dark blue bg, - light blue bg

A handful of illustration files break that 5-variant pattern — flagging rather than silently renaming, since I can't tell which file is meant to be authoritative in the ambiguous cases:

  • Cloud Service Models has 6 files, not 5: both - dark blue bg.png/ - light blue bg.png (the standard pair) and - dark blue.png/ - light blue.png (no bg) — but no plain - dark.png/- light.png like every other illustration has. Looks like the plain variants got exported under the wrong name.
  • TAISE Group Training has 6 files: both - white.png and - white bg.png, where every other illustration only has one.

Two more were resolved during the lowercase/dash filename cleanup: Virtual Event's -dark-blue-bg-copy.png/-light-blue-bg-copy.png were checked against their non-copy siblings — both copy files actually rendered on a plain white/transparent background, not the blue background their filename claimed (visually identical to virtual-event-white-bg.png), so they were mislabeled duplicates, not a real second export; deleted rather than renamed. AI Threat Radar and Blueprint's -dark_1.png/-light_1.png were confirmed to be the real dark/light variants (no competing file existed) and renamed to the plain -dark.png/-light.png pattern every other illustration uses.

Class naming

Every class ships prefixed csa- (csa-btn, csa-card, csa-kicker, …) so it can never collide with cloudsecurityalliance.org's own CSS once a page built with this library gets uploaded there. data-surface="..." is a plain data attribute, not a class — already collision-safe, left unprefixed.

*, html, body, a, and section in base/reset.css are bare element selectors — the reset approach CSA's page templates commonly used when this library was first built. This is intentional, not an oversight: this library is meant for whole standalone pages, not fragments dropped into an existing page, so there's no host content for a bare selector to collide with.

Tokens

7 color families (not 6 — the spec's palette includes Neutral alongside Blue/Orange/Teal/Purple/Green/Red), each an 11-step ramp 25–800, named --{letter}{ramp} (e.g. --b500, --o700, --n25). No semantic aliases (--csa-blue, --navy, --ink, etc.) — see the spec's retirement map if you're porting old page code. Shadows: only --shadow-sm and --shadow exist, per spec. Layout: --maxw (1200px) + .csa-wrap / .csa-section utilities. Headings: h1–h3 are weight 900, h4 is 700 — the one heading level that isn't unified to 900.

Components

File Source
grid.css .csa-grid / .csa-cell — CSS Grid system equivalent to Bulma's .grid / .cell. Defaults to 12 columns; .csa-grid-{2–6} presets for common card grids; --col-count inline for any other count. .csa-cell-col-{1–12} for column spans, .csa-cell-row-{1–6} for row spans. Supports true 2D layouts unlike .csa-columns (flexbox). Responsive: mobile-first breakpoint prefix classes — .csa-grid-{sm|md|lg}-{1–6} changes column count at ≥576/768/1024 px; .csa-cell-col-{sm|md|lg}-{1–12} changes a cell's span. Typical card grid: csa-grid-1 csa-grid-md-2 csa-grid-lg-4. Typical sidebar: csa-grid-1 csa-grid-lg-4 with csa-cell-col-lg-1 / csa-cell-col-lg-3 on the cells (25%/75% split).
surfaces.css Built fresh per spec — light/gradient/dark backgrounds + surface-aware .csa-card
cards.css .csa-card shell synthesized from a handful of bespoke card patterns seen across CSA's early pages (no shared class existed at the time); .csa-card-glass adds a 16px radius and surface-aware text color on top of the spec's base CSS. Also: .csa-card-hover (lift/shadow/border-color modifier, matching the hover treatment consistently used across CSA's early interactive cards), .csa-card-media (16:9 image-top card, surface-aware like .csa-card), .csa-card-reveal (4:3 photo card — kicker + heading sit permanently on a thin frosted-glass strip at the bottom; .csa-card-reveal-more/-more-inner grow that strip on hover via a grid-template-rows 0fr→1fr transition, not max-height, so it fits body copy of any length without a guessed cutoff; always dark regardless of page surface), .csa-go (shared CTA link utility, font-size:14.5px to read as a peer of body copy rather than a caption — --b500 default, --b300 on hover and on dark surfaces; promoted out of carousel.css where it started), .csa-card-brand-cloud/-ai/-zt (one per CSA sub-brand, white-background only, no data-surface variants — top-down fill + gradient border via the same double-background-clip trick buttons.css uses for a gradient border, plus a non-uniform border-width and a hard gradient stop so the border-box layer also carries a solid 4px top accent bar; border gradient (not the top accent, not the fill) swaps colors on :hover, and .csa-go is recolored to match each brand's own accent), .csa-card-quote (testimonial card — quote glyph, body copy, then a footer with a headshot slot, name, and title; the headshot is a plain <img>, not a mask, since a photo doesn't need to recolor with surrounding text the way an icon glyph does), and .csa-card-raised (always-on elevation for a card that needs more emphasis than the rest of its grid — swaps the base --shadow-sm for the bigger --shadow token, the same shadow .csa-card-hover only shows on :hover; background/border are untouched, so it's a pure modifier always paired with .csa-card; dark/black reuse .csa-card-hover's own black shadow value rather than a new one). An icon as a card's first child just needs .csa-icon — no new class
buttons.css .csa-btn-primary is always --o500 — no blue variant, no surface-conditional switching. 4 standardized variants, all surface-aware (work unmodified on light/gradient/dark/black): .csa-btn-primary, .csa-btn-secondary, .csa-btn-tertiary, .csa-btn-disabled. .csa-btn-light/.csa-btn-ghost/.csa-btn-onnavy are retired — Disabled replaces Light, Secondary is Ghost renamed (it already worked on dark surfaces unmodified, making Onnavy redundant there). Replaces .btn-gold/.btn-gold-outline (removed). Disabled's colors are surface-aware per surface, not just light-vs-dark: black uses its own n800/n500 values (matching the n-scale .csa-card already uses for its own black-surface background/border), rather than reusing dark's blue-scale b800/b500 on a surface that's otherwise neutral, not navy
action.css A surface-aware interactive tile for <a> or <button> — shares .csa-card's border treatment (n50 resting border, matching .csa-pagination-btn's convention). aria-selected="true" (the same attribute used for selected items in listboxes/grids/tab lists) switches it to an orange border + warm tint, plus a resting --shadow drop shadow (dark/black get their own higher-contrast black shadow, same technique as .csa-card-hover's dark override in cards.css) so a selected tile reads as chosen even before a pointer gets near it — deliberately static once selected, with no further color/lift shift on :hover, since a second signal on top of an already-selected tile read as redundant. Dark surface additionally gets a backdrop-filter: blur(3px). .csa-action-arrow adds a trailing chevron that tracks every state the border goes through via currentColor. Wrap multiple in .csa-actions to stack them in a full-width column, 10px gap
badges.css The original extracted-as-is glass pill (.csa-badge-hot, the spec's "Eyebrow") has been retired. Ships .csa-badge-primary/-secondary/-outlined instead — designed fresh, icon + italic label, surface-aware (light/gradient share one treatment, dark/black another). Icon is either a plain <img> (when a pre-colored file exists for the exact color, e.g. Calendar's b500/b100/b300/white variants) or the shared .csa-icon-glyph mask primitive (base/icon-glyph.css) when it doesn't
nav.css Extracted as-is — two real variants, see below. Not shown in the showcase (removed per your request) — the component still ships, it's just not documented on the showcase page
hero.css Built fresh per spec, informed by the shared real hero structure; also houses .csa-cta (the closing promo band, a pattern repeated across most of CSA's early program pages). .csa-hero-glass/-light/-dark nest a plain .csa-card-glass inside a .csa-hero-photo hero — recreates an earlier glass-panel-over-photo hero treatment used on a couple of CSA's early pages before it was later simplified to a plain gradient (identical values to .csa-card-glass's own dark variant); every text color inside is set explicitly rather than left to the .csa-hero/.csa-card-glass cascade, since nesting one inside the other creates real specificity collisions between them. .csa-hero-split-graphic — promoted from the STAR Program's "Learn about STAR" hero, reused as-is across 4 of its pages before landing here — has no glass panel; content floats directly on the photo, and .csa-wrap itself becomes a plain 2-column grid (1.2fr auto, 48px gap) with a single graphic (.csa-hero-split-img, a logo/wordmark, not an overlapping illustration) vertically centered on the right; below 1050px it drops to one column, image centered and capped at 200px
icons.css Sizing only (default 48px, .csa-icon-small 32px, .csa-icon-large 56px) — color/background treatment is baked into each SVG file in src/assets/icons/, see "Icons" below. Also documents the b500/b300/b100/n400/o300/o700 token-color subfolders (badge/tag use)
carousel.css Visuals extracted as-is from an early drag/momentum carousel built for one of CSA's program pages, generalized to support multiple carousels per page. .csa-carousel is the outer wrapper — a flex column stacking .csa-carousel-row (the arrow/viewport/arrow flex row, its own class now) above .csa-carousel-dots, so copying the one .csa-carousel block always brings the dots with it; they used to be a sibling after .csa-carousel instead of a child, which a real page's dev team missed entirely when copying just the block that looked like "the carousel." Card background is white (not --n25) so it reads against a gray section. Cards run narrow enough that the next one peeks into view instead of using an edge fade to hint "more to scroll" — no --csa-carousel-bg/background-matching to keep track of on whatever section it sits in. .csa-carousel-dots/.csa-carousel-dot/.csa-carousel-dot-active are an optional progress-dots row below .csa-carousel-row; which dot is active is real app state (tracks scroll/drag position), same as the rest of the carousel's behavior. No shipped JS — see "Styling only, not behavior" below; the drag/momentum/infinite-loop/dot-sync interaction is a BEHAVIOR SPEC comment at the top of the file, not code
tabs.css Designed fresh — no tabs UI existed elsewhere in the brand system at the time (checked for tablist/tab-panel patterns; none found). .csa-tab:hover gets a rounded n50 background (b600/n700 on dark/black — one ramp step lighter than that surface's own page background, not darker, so the hover reads as a lift rather than a hole; n50 rather than n25 on light so it still shows up against an n25 page). The fill lives on a ::before inset short of the tab's own bottom edge, not the tab's own background, so the rounded highlight floats above .csa-tablist's border-bottom (or the active tab's own indicator) instead of touching it. No shipped JS — see "Styling only, not behavior" below; the WAI-ARIA tabs interaction is a BEHAVIOR SPEC comment at the top of the file, not code
toggles.css Two switch patterns extracted from RiskRubric.ai (an existing CSA property): .csa-toggle-icon (compact switch, icon per state — that page's own dark-mode switch, thumb slide added since the reference's own thumb doesn't move) and .csa-toggle-segmented (two-option segmented control with a sliding highlight — that page's "10 highest/lowest scoring" filter). Segmented toggle colors copied exactly as they render there; the icon toggle's resting-track color is flat library tokens instead (n25/n50/b800 by surface) rather than the reference's own semi-transparent value, for consistency with the rest of the library's "one ramp step off the page" convention. .csa-toggle-icon-confirm is a second icon-toggle variant on the same button — X/checkmark instead of sun/moon, green (--g200/--g100) instead of orange for "on" — for a generic enable/disable toggle rather than one specifically about dark mode. No shipped JS — the entire visual state of both is driven by one attribute (aria-pressed / data-selected) the app flips on click, same minimal contract as .csa-tag-filter
tables.css Built fresh per your spec — a generic data table: n50 grid lines, a b500 header row, alternating white/n25 body rows. .csa-table-wrap exists solely to round the table's corners (12px) — overflow:hidden on a separate wrapper, since a <table> with border-collapse:collapse (needed for single, non-doubled n50 lines between cells) doesn't reliably respect its own border-radius across browsers

Nav has two real variants

  1. Hub nav — logo lockup + link row + primary CTA button, the treatment used on hub-style landing pages.
  2. Subpage nav — logo lockup + a single "← back" link, used everywhere else.

Both are in nav.css. Nav (and footer) content is a placeholder in this library on purpose — the real site nav replaces it once a page goes live on cloudsecurityalliance.org.

Icons

Source: CSA Brand Guide 20260421.pdf, "Icon Color Variations" + "Approved Icon Color & Background Combinations" (pages 15–16). Default size is 48px, matching CSA's existing brand pages.

There are 6 style variants per icon in src/assets/icons/, and the color and background chip are baked directly into each SVG file — icons.css only handles sizing (.csa-icon, object-fit: contain to smooth over slightly inconsistent internal viewBoxes between an icon's chip and no-chip variants). The only real decision when placing an icon is which file to use for the background it's sitting on:

Style File suffix Approved backgrounds
Monochrome (default) (none) White, N25, B25–B50 tint, Gradient
Inverse Monochrome (default, dark) -white B700, N800
Light Blue (subtle grouping) -light-blue White, N25
CSA Blue (important) -CSA-blue White, N25, Gradient, B25–B50 tint, B700
Yellow Orange (attention) -yellow-orange White, N25, B700
Red Orange (bold moment — use sparingly) -red-orange B700 only

Rules from the brand guide: don't mix multiple icon styles within the same section; don't use low-contrast combinations; use color to communicate importance, not decoration. The showcase's Icons section renders this whole table live with a real icon (Shield-Keyhole) so it's a working reference, not just a static picture.

Token-color variants (badges, neutral tags)

A separate mechanism from the 6 approved combinations above, for contexts that need an icon to match an arbitrary surrounding color rather than one of the brand guide's fixed chip pairings — badge variants and neutral tags being the driving cases. src/assets/icons/ has 6 subfolders — b500/, b300/, b100/, n400/, o300/, o700/ — each holding the same 120 base icons recolored to that one token. Both the folder and the filename carry the color (calendar-b500.svg inside b500/, matching the pre-existing calendar-b100/-b300/ -b500.svg files this pattern was modeled on) — the folder groups them for browsing, the suffix keeps the color self-evident even if a file is ever copied out on its own: <img class="csa-icon" src="../assets/icons/b500/calendar-b500.svg">.

The 3 gradient sub-brand marks (ai-brand.svg, cloud-brand.svg, zero-trust-brand.svg) are excluded from every color folder — they're multi-tone marks built from a gradient fill, not single-color icons, so flattening them to one token color would break the mark rather than recolor it.

This is step one of three you asked for — establishing the color/background foundation. Layout examples that feature icons are still a follow-up; adding icons to badges and neutral tags now has its token-color source files (above) ready to use.

Border radius nesting

When a rounded component sits inside another rounded container, the inner radius should read as visibly smaller than the outer one — not equal, or the corners look like a coincidence rather than a system. The real precedent for this is .csa-card-glass itself: an earlier standalone use of this same glass-panel treatment was 16px, while a version nested inside another rounded container (a 20px-rounded band) used 12px. Default .csa-card-glass to the 16px standalone case; scale it down (e.g. 12px) if you nest it inside another rounded container. The showcase's own demo wrappers (.docs-surface-demo, .docs-glass-demo, 20px) follow the same rule around the 16px .csa-card / .csa-card-glass they contain.

Flagged conflicts / decisions made while building

These came up while reconciling the spec against real markup from CSA's early program pages — resolved per your direction, noted here for visibility:

  • No tabs component existed elsewhere in the brand system yet. Per your direction, tabs.css was designed fresh, following the system's existing conventions (underline-indicator style, data-surface-aware active color mirroring .csa-btn-primary) rather than extracted.
  • .btn-primary color disagreed across CSA's early pages: blue on one, hardcoded orange on the others. Per your call, resolved to always --o500 — no blue variant, no surface-conditional switching.
  • Hero/CTA kicker color: .csa-hero/.csa-cta are always-dark bespoke backgrounds, not tied to data-surface, so the generic [data-surface="dark"] .csa-kicker rule never reached them — wired directly in hero.css instead. Per your call, the two are split to match their real precedent: .csa-hero .csa-kicker/.csa-kicker-sub is --b300 (those early pages hardcoded the hero kicker to blue, #0093ff), while .csa-cta's stays --o500 (matching the other dark blocks on those same pages — advisory/mission/sage — which use orange).
  • .csa-card's shape (radius/padding/shadow) has no single spec value — none of those early pages used a shared .card class, each had bespoke one-off variants in the 14–16px radius / 28–32px padding range. Picked 16px / 32px / --shadow-sm as a synthesis of that range.
  • .csa-badge-hot (the original extracted glass pill) is retired. It wasn't used anywhere in the showcase, only mentioned in passing text, so it was dropped in favor of documenting just the Primary/Secondary/ Outlined set it was never actually shown alongside.
  • .csa-tag-icon-glyph/.csa-badge-icon-glyph were merged into one shared .csa-icon-glyph primitive (base/icon-glyph.css) — both were the identical mask + currentColor technique with only the custom-property name and size differing, which is exactly how their sizes (14px vs 13px) drifted out of sync before being caught. Each component now only sets its own size, scoped under its own ancestor class, on top of the one shared definition. Known limitation: mask-image doesn't render under the file:// protocol (confirmed by testing) — fine for a real deployed page, but a blank glyph if dist/csa-library.html/dist/preview/index.html is opened by double-click instead of served. See the comment on .csa-icon-glyph in base/icon-glyph.css for the workaround (inline the SVG with fill="currentColor" instead) if that matters for your use case.
  • h3/h4 stay unset at the token level (typography.css) — deliberate, not an oversight. Unlike h1/h2 (each only ever appears in one shape), h3 already needs a different size in nearly every context it's used in: a standard .csa-card heading, a .csa-card-reveal strip's compact heading, a .csa-card-glass panel's heading, a carousel card title (.csa-carousel-card h3, in carousel.css) — one library-wide h3 size would be wrong for most of those. What WAS wrong: each of those contexts had its font-size/margin repeated as an inline style on every individual heading in the docs (drifting between 19px/20px with no real reason), instead of being scoped once per context. Consolidated into .csa-card h3, .csa-card-glass h3, and .csa-card-reveal-head h3 in cards.css, plus #surfaces h3 in the docs page's own <style> block for the Surfaces section's demo-only heading (not a real component usage, so it doesn't belong in cards.css). h4 has no real usage yet (appears once, in the type-scale demo itself) — nothing to consolidate there.

About

okens, base styles, and components for building on-brand CSA pages, apps, and prototypes. Every class below is prefixed csa- so it never collides with cloudsecurityalliance.org's own CSS.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages