Skip to content

Hum2a/501Fun-feature-cards

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

132 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

<feature-cards>

501 technical brief submission: SUBMISSION.md — problem statement, methodology, live demo, and portfolio links for reviewers.

CI License: AGPL-3.0-only npm version Bundle size

Package version: 1.1.1 Live demo

Package: @techystuff/feature-cards · Version: 1.1.1
Install guide: docs/INSTALL.md · Live demo: 501fun.humza-butt.space
Documentation hub: docs/README.md

One accessible, responsive, CMS-agnostic Web Component that replaces hard-coded feature-card images. Native browser APIs — Shadow DOM, container queries, constructable stylesheets — authored in strict TypeScript, shipped as zero-framework vanilla JS (~25 KiB gzip).

Three themed feature cards rendered by the component

Table of contents

Why this exists

You need… This provides…
501 landing-page stat cards (replace designer PNGs) layout: "stat" + live demo card editor
Edit top / middle / bottom / icon without a designer Four-field schema + editor UI
Per-card colour, rotation, and size appearance + 501-* themes
Drop-in embed for any CMS Custom Element + IIFE script tag
No framework lock-in Vanilla JS core; optional React wrapper
Headless content src fetch + WordPress / Contentful / Sanity adapters
No-JS fallback Plain <a> children upgrade progressively
Style safety on unknown pages Shadow DOM + --fc-* token theming API
Sidebar and full-width layouts Container queries — not viewport breakpoints
Proof of quality axe zero violations in CI; 90%+ unit coverage

Quick start

Script tag (any CMS, no build step)

<script src="https://cdn.jsdelivr.net/npm/@techystuff/feature-cards@1.1.1/dist/feature-cards.iife.js" defer></script>

<feature-cards heading="Why teams choose us">
  <script type="application/json">
    {
      "cards": [
        {
          "id": "satisfaction",
          "title": "Satisfaction that sticks",
          "figure": { "value": "97%", "label": "customer satisfaction", "trend": "up" },
          "cta": { "label": "Read customer stories", "href": "/customers" },
          "theme": "brand-blue"
        }
      ]
    }
  </script>
</feature-cards>

Pin versions and add SRI — see WordPress cookbook.

Install from npm

Full guide: docs/INSTALL.md — npm vs CDN, all usage patterns, exports, licence, smoke test.

npm install @techystuff/feature-cards@1.1.1
Entry Import
Web Component import '@techystuff/feature-cards'
Imperative API import { createFeatureCards } from '@techystuff/feature-cards'
React wrapper import { FeatureCards } from '@techystuff/feature-cards/react'
CDN (no build) See INSTALL.md § CDN

AGPL-3.0-only — commercial licence for closed-source use.
Maintainers: docs/NPM-PUBLISH.md.

ESM

import '@techystuff/feature-cards';

const el = document.querySelector('feature-cards');
el.data = {
  cards: [{ id: 'a', title: 'Hello', cta: { label: 'Go', href: '/go' } }],
};

Progressive enhancement (JS disabled)

<feature-cards heading="From plain links">
  <a href="/docs" data-eyebrow="Guides" data-description="Integrate in an afternoon">
    Read the documentation
  </a>
</feature-cards>

Headless CMS

<feature-cards src="https://cms.example.com/api/cards" adapter="wordpress"></feature-cards>

Imperative mount

import { createFeatureCards } from '@techystuff/feature-cards';

createFeatureCards({
  target: '#cards-host',
  src: 'https://cms.example.com/api/cards',
  adapter: 'wordpress',
  onReady: ({ count }) => console.log(`Rendered ${count} cards`),
});

React (optional peer)

import { FeatureCards } from '@techystuff/feature-cards/react';

<FeatureCards
  data={{ cards: [{ id: 'a', title: 'Hello', cta: { label: 'Go', href: '/go' } }] }}
  onCardClick={({ id }) => console.log(id)}
/>

Data model

All sources normalise to one JSON shape. Full reference: docs/SCHEMA.md.

501 stat module (primary brief)

Editor label JSON field
Top text eyebrow
Middle text figure.value
Bottom text figure.label
Icon / image media.src + alt

Use layout: "stat" and optional appearance for colours, rotation, scale, and min-height. Try the 501 feature cards — live editor at the top of the demo page.

{
  "cards": [
    {
      "id": "guests",
      "layout": "stat",
      "eyebrow": "More than",
      "figure": { "value": "12,000,000", "label": "delighted guests" },
      "media": { "src": "/icons/lucide/users.svg", "alt": "" },
      "theme": "501-green",
      "appearance": { "background": "#c6f135", "rotateDeg": 0, "scale": 1 }
    }
  ]
}

Standard marketing card

{
  "heading": "Optional section title",
  "cards": [
    {
      "id": "unique-id",
      "layout": "standard",
      "title": "Required card title",
      "eyebrow": "Optional kicker",
      "description": "Supporting copy",
      "figure": { "value": "97%", "label": "customer satisfaction", "trend": "up" },
      "media": { "src": "/img.svg", "alt": "" },
      "cta": { "label": "Learn more", "href": "/path" },
      "theme": "brand-blue"
    }
  ]
}

Precedence (highest wins): data property → inline JSON → src + adapter → light-DOM links.

Public API

Attributes

Attribute Type Default Description
src URL JSON endpoint to fetch card data
adapter generic | wordpress | contentful | sanity generic CMS payload mapper
columns 16 auto-fit Caps grid track count
heading string from data Section heading (overrides data heading)
heading-level 16 2 Section heading level; card titles = level + 1
theme brand-blue | brand-green | brand-amber Host theme (per-card theme in data wins)

Properties

Property Type Description
data FeatureCardsData | undefined Validated data; highest precedence. Reads back current render state.
provenance object (readonly) Inert authorship record (UUID, repo, licence)

Events (bubble, composed)

Event detail When
featurecards:ready { count } Render completed
featurecards:error { issues[], problem } Invalid data or fetch failure — never throws
featurecards:cardclick { id, card } Card link activated

Slots

Slot Purpose
(default) No-JS fallback / invalid-data fallback
heading Replace generated section heading

Theming — CSS custom properties

Token Purpose Token Purpose
--fc-font Font stack --fc-card-min Min card track width
--fc-bg Section background --fc-gap Grid gap
--fc-fg Primary text --fc-pad Card padding
--fc-muted Secondary text --fc-radius Corner radius
--fc-accent Accent colour --fc-shadow / --fc-shadow-hover Elevation
--fc-card-bg Card background --fc-ring Focus ring
--fc-card-border Card border --fc-transition Motion timing

CSS parts

::part(section) · ::part(heading) · ::part(grid) · ::part(card) · ::part(link) · ::part(eyebrow) · ::part(title) · ::part(description) · ::part(figure) · ::part(value) · ::part(label) · ::part(media) · ::part(cta)

feature-cards {
  --fc-accent: rebeccapurple;
  --fc-radius: 4px;
}
feature-cards::part(card):hover {
  outline: 2px solid rebeccapurple;
}

TypeDoc: npm run docs:apidocs/api/ (CI uploads artefacts).

CMS integration

One canonical schema; adapters map CMS JSON into it.

<!-- WordPress REST (+ _embed for media) -->
<feature-cards src="https://site.com/wp-json/wp/v2/posts?_embed" adapter="wordpress"></feature-cards>

<!-- Contentful Delivery API -->
<feature-cards src="https://cdn.contentful.com/spaces/SPACE/entries?content_type=card&access_token=TOKEN" adapter="contentful"></feature-cards>

<!-- Sanity GROQ HTTP API -->
<feature-cards src="https://PROJECT.api.sanity.io/v2024-01-01/data/query/production?query=..." adapter="sanity"></feature-cards>

<!-- Already canonical JSON -->
<feature-cards src="/api/cards" adapter="generic"></feature-cards>
Guide Topics
WordPress functions.php, REST, imperative mount, SRI
Contentful Content model, Delivery API, webhook → static JSON
Sanity GROQ, HTTP API, Studio preview workflow

New CMS = one ~40-line adapter + registry line. Mock Worker OpenAPI: live schema · source.

Stuck?docs/TROUBLESHOOTING.md · docs/FAQ.md

Demo page themes and motion

The live demo includes a Vibe check picker — twelve parody-named page themes (Corporate Daydream™, Pager Duty Noir, …). Themes swap via --page-* CSS properties with animated crossfade; choice persists in localStorage (fc-page-theme).

Scroll reveals, hero stagger, schema validation flashes, and component enter/hover animations live in demo/motion/. Both layers honour prefers-reduced-motion.

Note: Page themes are demo-only — not part of the npm package. Production sites use --fc-* component tokens. Full guide: docs/DEMO.md.

Accessibility

Semantic structure, configurable heading levels, single-link cards, trend announcements, keyboard-native interaction, reduced-motion and high-contrast support, and an axe-core CI gate at zero violations.

Topic Document
WCAG mapping, keyboard, SR behaviour ACCESSIBILITY.md
FAQ docs/FAQ.md
Tests docs/TESTING.md

Architecture & methodology

ADR Decision
0001 Custom Element over framework
0002 Shadow DOM encapsulation
0003 Zod schema + adapters
0004 AGPL + canary provenance
0005 Container-query layout
0006 Demo themes & motion

Narrative: ARCHITECTURE.md · Diagrams: docs/diagrams/architecture.md

Development

npm run setup        # first-time: env, deps, Playwright, build:lib, doctor
npm run dev          # demo → http://localhost:5173
npm run serve:cms    # mock CMS → http://localhost:8787/api/cards (second terminal)
npm run check        # typecheck + lint + format + tests + size

Tooling

Command Output
npm run docs:api TypeDoc → docs/api/
npm run cem / cem:check Custom Elements Manifest
npm run sri IIFE Subresource Integrity hash
npm run validate:cms -- <url> Smoke-test CMS JSON
npm run rules:sync Mirror agent rules to .claude/ + .agents/
Artefact Purpose
custom-elements.json CEM — IDE autocomplete
.vscode/html-custom-data.json VS Code tag hints
docs/api/ Generated API reference

Contributors: CONTRIBUTING.md · Agents: AGENTS.md

Testing

npm run test:unit       # Vitest
npm run test:contracts  # MSW adapter contracts
npm run test:fuzz       # Property-based schema
npm run test:a11y       # axe zero violations
npm run test:e2e        # Playwright demo flows
npm run test:visual     # Screenshot regression (Chromium)
npm run test:browser    # Web Test Runner
npm run check           # All gates

Full matrix: docs/TESTING.md

Releasing

npm run release:current
npm run release:patch              # bump + changelog + tag
npm run release:minor              # 1.0.x → 1.1.0
npm run release:patch -- --publish # tag + npm publish (use --otp= for 2FA)

Playbook: docs/RELEASE.md · Stable v*.*.* tags publish to npm via publish-npm.yml (trusted publishing or NPM_TOKEN).

Deployment

Production demo: 501fun.humza-butt.space

Service URL
Demo (Cloudflare Pages) https://501fun.humza-butt.space
Mock CMS (Worker) https://cms.501fun.humza-butt.space/api/cards

Push to master triggers CI deploy (see config/site.json). PRs receive *.pages.dev preview URLs.

One-time setup

  1. DNS501fun.humza-butt.space on Cloudflare zone humza-butt.space
  2. GitHub secretsCLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID
  3. Token permissions — Pages Edit, Workers Edit, Zone DNS Edit

Local deploy uses .env via scripts/run-wrangler.mjs — not wrangler login OAuth.

Manual deploy

npm run deploy
npm run canary:verify -- https://501fun.humza-butt.space

Documentation index

Document Description
docs/README.md Master documentation hub
docs/SCHEMA.md Canonical JSON reference
docs/FAQ.md Frequently asked questions
docs/TROUBLESHOOTING.md Symptom → fix guide
docs/TESTING.md Test matrix and conventions
docs/DEMO.md Demo page themes & motion
docs/RELEASE.md Release playbook
docs/BRANCHING.md Branch strategy
ARCHITECTURE.md Design narrative
ACCESSIBILITY.md a11y specification
SECURITY.md Security & canary verification
CHANGELOG.md Version history

Licence

AGPL-3.0-only — © 2026 Humza Butt. See LICENSE and NOTICE.

You may read, run, and evaluate freely. Deploying modified versions as a network service requires offering corresponding source under the same licence. Commercial closed-source use requires a commercial licence.

Document Purpose
LEGAL.md Enforcement guide — evidence, takedowns, contacts
TRADEMARK.md Name and logo usage policy
TERMS_OF_USE.md Live demo site terms
SECURITY.md Canary watermark verification

Inert authorship markers are verifiable: npm run canary:verify -- <url>.

About

One accessible, responsive, CMS-agnostic Web Component that replaces hard-coded feature-card images. Native browser APIs only - Shadow DOM, container queries, constructable stylesheets - authored in strict TypeScript, shipped as zero-framework vanilla JS.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors