@a.nemreen/dga-tokens
v0.3.8
Published
DGA design tokens & Tailwind v4 theme for Angular — RTL/Arabic-first CSS variables, light/dark, fonts, and brand themes
Maintainers
Readme
@a.nemreen/dga-tokens
Design tokens and Tailwind v4 theme for DGA (Digital Government Authority) Angular apps — CSS variables, semantic colors, spacing, type scale, and Angular services for theme, locale (RTL/LTR), fonts, and brand.
Pair with @a.nemreen/dga-ui for components.
Live demo: nemreen.info/dga
Version 0.2.x — API may still evolve until 1.0.0.
Changelog
CHANGELOG.md ships with this package. Live notes: nemreen.info/dga/docs/changelog.
Features
- Semantic CSS custom properties (palette → primitives → semantic → theme)
- Tailwind v4
@themebridge for utility classes - Light / dark via
DgaThemeService(data-theme) - Configurable locales + RTL/LTR via
DgaLocaleService/provideDgaLocales(lang/dir) - Arabic font switching via
DgaFontService - Brand color ramps via
DgaBrandService - Optional content icon font (
styles/icon-font.css) — HugeIcons Stroke Rounded
Install
npm install @a.nemreen/dga-tokensRequires Angular 19+ and Tailwind CSS v4 (tailwindcss + @tailwindcss/postcss).
Public entry points
| Import | Purpose |
|--------|---------|
| @a.nemreen/dga-tokens | DgaThemeService, DgaLocaleService, provideDgaLocales, DgaFontService, DgaBrandService |
| @a.nemreen/dga-tokens/styles.css | Full token + Tailwind @theme stylesheet (includes host-compat.css) |
| @a.nemreen/dga-tokens/styles/host-compat.css | Unlayered isolation vs host * / a / button globals (already in styles.css) |
| @a.nemreen/dga-tokens/styles/icon-font.css | Optional HugeIcons Stroke Rounded font for content icons |
| @a.nemreen/dga-tokens/styles/prefs-boot.js | Allowlisted FOUC boot script for index.html |
Do not depend on undocumented files under styles/ except the entries above.
Styles
@import 'tailwindcss';
@import '@a.nemreen/dga-tokens/styles.css';
/* or, with some tooling: */
@import 'dga-tokens/styles.css';styles.css ends with unlayered host-compat.css. That scoped sheet beats leftover host globals such as * { margin: 0; padding: 0 } and a { color: var(--color-mainColor) } inside dga-* components and [dgaButton] / [dgaLink] hosts. You do not need to delete those rules for DGA UI to layout and color correctly.
Still wrap host leftovers in @layer base if your own Tailwind classes should also win (the * reset is unlayered and otherwise beats @layer utilities on the rest of the page):
@layer base {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
color: var(--color-mainColor);
font-weight: 400;
transition: color 0.2s ease-in-out;
}
}Content icon font (optional)
For icons in editorial content, feature grids, and lists, opt into the self-hosted Stroke Rounded font. Chrome controls should keep using <dga-icon name="…"> (inline SVG from HugeIcons) so they paint without waiting on the font.
@import '@a.nemreen/dga-tokens/styles/icon-font.css';<i class="hgi-stroke hgi-search-01" aria-hidden="true" style="font-size: 24px"></i>
<!-- or -->
<dga-icon glyph="search-01" size="xl" />Glyph names match hugeicons.com (Stroke Rounded). The font carries ~4k glyphs; a name that is missing from the font renders nothing. See styles/THIRD-PARTY-ICONS.md for the MIT notice.
Typography (Arabic Google Fonts)
Platforms Code defaults to IBM Plex Sans Arabic at weights 400 / 500 / 600 / 700. Tokens declare the family; apps must load the font files.
Switch families at runtime with DgaFontService (sets data-font on <html>):
const font = inject(DgaFontService);
font.setFont('cairo'); // ibm-plex | noto-sans | cairo | tajawal | almarai | changa | readex-pro | amiriBrand themes
Switch the primary color ramp with DgaBrandService (sets data-brand on <html>). Independent of light/dark.
const brand = inject(DgaBrandService);
brand.setBrand('blue'); // green | blue | purple | gold | teal | hajj | foundation | national
// Seasonal brand IDs remap primary colors only; occasion imagery lives in the app/demo layer.Preference persistence
These services save to localStorage and restore on load:
| Key | Service | Values |
|-----|---------|--------|
| dga-theme | DgaThemeService | dark (light = key removed) |
| dga-locale | DgaLocaleService | registered locale code (default ar | en) |
| dga-locale-dir | DgaLocaleService | companion rtl | ltr for FOUC boot |
| dga-font | DgaFontService | font id (default ibm-plex = key removed) |
| dga-brand | DgaBrandService | brand id (default green = key removed) |
In production apps, apply prefs before first paint with the shipped boot script (allowlists font/brand IDs):
<script src="node_modules/@a.nemreen/dga-tokens/styles/prefs-boot.js"></script>Or inline the same logic from that file. Services also clear invalid data-font / data-brand attributes on hydrate and skip DOM/storage work during SSR.
Locales (multi-language + RTL/LTR)
Default languages are Arabic (rtl) and English (ltr). Register more in app.config.ts — each entry sets its own direction:
import { provideDgaLocales, DgaLocaleService } from '@a.nemreen/dga-tokens';
export const appConfig: ApplicationConfig = {
providers: [
provideDgaLocales([
{ code: 'ar', dir: 'rtl', nativeName: 'العربية' },
{ code: 'en', dir: 'ltr', nativeName: 'English' },
{ code: 'ur', dir: 'rtl', nativeName: 'اردو' }, // Urdu = RTL
]),
],
};
const locale = inject(DgaLocaleService);
locale.locales; // registered list (for language pickers)
locale.setLocale('ur'); // applies lang + dir from the registry
locale.cycleLocale(); // next in the listDirection always comes from the registry (dir), never from “is Arabic?”. Localized labels should be maps keyed by code:
{ ar: '…', en: '…', ur: '…' } // missing keys fall back to en, then arLoad IBM Plex Sans Arabic (400–700) off the critical path so the Google Fonts CSS does not block first paint. DgaFontService injects other curated families when the user switches (prefs-boot.js does the same before paint for a stored choice):
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="preload"
as="style"
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@400;500;600;700&display=swap"
/>
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@400;500;600;700&display=swap"
rel="stylesheet"
media="print"
onload="this.media='all'"
/>
<noscript>
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
</noscript>Self-hosting the same weights is preferred for production government apps.
Type scale utilities: text-display-* / text-body-* (including text-body-2xs at 10px/14px).
Content width
Portal shells use a fluid content column with side gutters:
| Token | Value | Role |
|-------|-------|------|
| --dga-content-max-width / --container-7xl | 1280px | Content column max width |
| --dga-viewport-padding | 32px (16px < 600px) | Minimum side gutter |
| --dga-wrapper-padding / --wrapper-padding | max(viewport, (100% − 1280px) / 2) | Actual padding-inline |
Use w-full px-(--wrapper-padding) on shells. Do not pair max-w-(--container-7xl) with large inner padding (lg:px-8xl) - that double-shrinks the content column.
Breakpoints
Aligned with layout & spacing:
| Token | Min-width | Guideline |
|-------|-----------|-----------|
| sm | 600px | Small / mobile |
| md | 960px | Medium / tablet |
| lg | 1280px | Large / desktop |
| xl | 1440px | X large / desktop |
These override Tailwind’s default screens via @theme (literal px values - required so @media queries work). md: / lg: therefore follow DGA.
Spacing
Named scale (none … 11xl) plus large layout numerics (48 … 480, e.g. p-48, max-w-(--spacing-320)). Paragraph measure: --dga-paragraph-max-width → 720px (spacing-180).
Theme service
import { DgaThemeService } from '@a.nemreen/dga-tokens';
inject(DgaThemeService).toggle();License
MIT — see LICENSE. Copyright (c) 2026 Ahmed Nemreen.
Use it in any project. Do not rebrand and sell it as your own unique product.
