@fat-karting-league/design-system-angular
v0.3.0
Published
Reusable Angular components and design foundations for FKL products.
Readme
FKL Design System — Angular (fkl-ds)
@fat-karting-league/design-system-angular is FKL's Angular component library. It mirrors the DOM structure, CSS classes and data-fkl-ds-* attributes of @fat-karting-league/design-system (the React package), so both frameworks share a single compiled stylesheet.
Colour, typography and button styles follow the FKL styleguide. Button variants are primary (blue fill), secondary (white with black border), tertiary (underlined link style), plus ghost and destructive.
Installation
npm install @fat-karting-league/design-system-angular@angular/core and @angular/common (^19.0.0 || ^20.0.0) are peer dependencies. Consumers do not need Tailwind CSS.
import { Component } from "@angular/core";
import { ButtonComponent, HeadingDirective, TextDirective } from "@fat-karting-league/design-system-angular";
@Component({
selector: "app-example",
standalone: true,
imports: [ButtonComponent, HeadingDirective, TextDirective],
template: `
<section>
<h1 fklDsHeading [level]="1">Account</h1>
<p fklDsText>Manage your account details.</p>
<button fklDsButton type="button">Save changes</button>
</section>
`,
})
export class ExampleComponent {}import "@fat-karting-league/design-system-angular/styles.css";A note on the component API
Every component in this package is designed to render the exact same DOM as its React counterpart: the same tag, the same fkl-ds-* classes and the same data-fkl-ds-* attributes. Angular cannot swap a component's host tag at runtime the way React's createElement can, so most components attach to a native element you write yourself, rather than introducing a custom wrapper tag:
<!-- Text and Heading take the place of React's `as` prop / dynamic tag -->
<p fklDsText size="small">Body copy</p>
<h3 fklDsHeading [level]="3">A sub-heading</h3>
<!-- Button renders as whichever tag you choose (no `asChild`) -->
<button fklDsButton variant="primary">Save</button>
<a fklDsButton variant="secondary" href="/events">View events</a>
<!-- Icons attach to a native <svg>, matching the React <Icon> output exactly -->
<svg fklDsIcon name="check" [size]="17"></svg>Components that build their own internal markup (Pill, Stats, Table, Section, Hero, cards, etc.) still attach to the semantic root tag React uses (span, div, section, article, figure…), so the top-level element and its classes always match. Inputs are declared with the Angular 19+ input()/output() signal APIs and are alphabetised on each component.
CSS entry points
Import the complete design system once:
import "@fat-karting-league/design-system-angular/styles.css";styles.css includes tokens, a minimal reset, document defaults, semantic HTML defaults, rich-content styles, component styles and accessibility styles — identical to the React package's stylesheet, because both packages re-export the same compiled CSS from @fat-karting-league/design-system-styles.
Import tokens without global element styles:
import "@fat-karting-league/design-system-angular/tokens.css";Import standalone rich-content tokens and styles:
import "@fat-karting-league/design-system-angular/content.css";This package ships no component-level CSS of its own — all visual styling comes from the shared stylesheet above. If your application already loads @fat-karting-league/design-system/styles.css (for example in a mixed React/Angular monorepo), you do not need to import it again from this package.
Components
| Component | Selector | Notes |
| --- | --- | --- |
| Text | p[fklDsText], div[fklDsText], span[fklDsText] | align, size |
| Heading | h1–h6[fklDsHeading] | level, visualStyle |
| Pill | span[fklDsPill] | check icon + projected label |
| Container | div[fklDsContainer] | padded |
| Icon | svg[fklDsIcon] | name, size, appearance |
| Stats | div[fklDsStats] | items (max 3) |
| Table | div[fklDsTable] | columns, rows, numbered |
| Timeline | div[fklDsTimeline] | steps |
| Button | button[fklDsButton], a[fklDsButton] | variant, size, appearance, loading, leading/trailing icon slots |
| Section | section[fklDsSection] | appearance, background image, gap, heading, padded |
| Hero | section[fklDsHero] | heading, imageSrc, highlights, description, actions via ng-content |
| Footer | footer[fklDsFooter] | bundled logo, links, tagline, copyright |
| CtaCard / CtaCards | article[fklDsCtaCard] / div[fklDsCtaCards] | action via ng-content |
| EventCard / EventCards | article[fklDsEventCard] / div[fklDsEventCards] | uses Button, Icon, Pill internally |
| ImageCard | figure[fklDsImageCard] | alt, src |
| QuoteCard / QuoteCards | figure[fklDsQuoteCard] / div[fklDsQuoteCards] | items (max 3) |
| Faq | div[fklDsFaq] | single-open accordion, items |
| FilterDropdown / FilterBar | button[fklDsFilterDropdown] / div[fklDsFilterBar] | see "Simplifications" below |
| Navigation | header[fklDsNavigation] | bundled logos, mobile sheet |
| ImageCarousel | section[fklDsImageCarousel] | auto-scrolling strip; items required (no bundled photos) |
| VideoCard | article[fklDsVideoCard] | YouTube/Vimeo/file URL parsing, lightbox player |
Simplifications versus the React package
A small number of React behaviours rely on Radix UI primitives (Dialog, Select, Accordion, Slot) that portal content to document.body, measure DOM nodes for animation and support asChild tag polymorphism. This package reimplements the same markup and CSS classes without a Radix/CDK dependency:
- Faq — single-open/collapsible behaviour and all classes/
data-stateattributes match; the animated open/close height transition (which depends on--radix-accordion-content-height, set by Radix's JS at runtime) is simplified to an instant show/hide. - Filter —
FilterDropdown's listbox is rendered inline (absolutely positioned) rather than through a floating-ui-positioned portal;FilterBar's mobile drawer is a fixed-position panel rendered in place rather than portalled. Project filter controls through anfklDsFilterBarContent<ng-template>, which is instantiated twice (mobile drawer + desktop row), mirroring how the React version renders{children}twice. - Navigation — the mobile menu sheet is a fixed-position panel rendered in place rather than portalled; behaviour, classes and
data-fkl-ds-*attributes match. - VideoCard — the lightbox is a fixed-position panel rendered in place rather than portalled.
- ImageCarousel — behaviour (auto-scroll, pause on hover, manual scroll controls, shuffle-once-on-init, reduced-motion respect) matches. The React package's bundled example Instagram photos are not shipped with this package to avoid packaging binary demo assets; pass
itemsexplicitly. - Button — there is no Angular equivalent of
asChild/Slot. Write the anchor tag directly when you need link behaviour (<a fklDsButton href="…">) instead of passing anasChildflag.
Development
Requires Node.js 20 or newer.
npm install
npm run build
npm run typecheckThe library build runs ng-packagr (see ng-package.json) and then copies the compiled styles.css, tokens.css, content.css and font assets from @fat-karting-league/design-system-styles into dist, exactly as the React package does.
Versioning and releases
The package is published publicly as @fat-karting-league/design-system-angular, versioned in lockstep with the React and styles packages.
