# `@a.nemreen/dga-tokens`

<p align="center">
  <img src="https://cdn.jsdelivr.net/npm/@a.nemreen/dga-tokens@0.2.1/cover.jpg" alt="DGA Angular — design tokens & UI" width="100%" />
</p>

[![npm version](https://img.shields.io/npm/v/@a.nemreen/dga-tokens.svg)](https://www.npmjs.com/package/@a.nemreen/dga-tokens)
[![license](https://img.shields.io/npm/l/@a.nemreen/dga-tokens.svg)](https://github.com/nemreen/dga-angular/blob/main/LICENSE)
[![Angular](https://img.shields.io/badge/Angular-19-DD0031?logo=angular&logoColor=white)](https://angular.dev)
[![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-v4-38B2AC?logo=tailwindcss&logoColor=white)](https://tailwindcss.com)
[![Live demo](https://img.shields.io/badge/demo-nemreen.info%2Fdga-1B8354)](https://nemreen.info/dga/)

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`](https://www.npmjs.com/package/@a.nemreen/dga-ui) for components.  
**Live demo:** [nemreen.info/dga](https://nemreen.info/dga/)

> Version **0.2.x** — API may still evolve until 1.0.0.

## Features

- Semantic CSS custom properties (palette → primitives → semantic → theme)
- Tailwind v4 `@theme` bridge 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`)

## Install

```bash
npm install @a.nemreen/dga-tokens
```

Requires 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

```css
@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):

```css
@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) so they paint without waiting on the font.

```css
@import '@a.nemreen/dga-tokens/styles/icon-font.css';
```

```html
<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](https://hugeicons.com/icons/stroke-rounded) (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>`):

```ts
const font = inject(DgaFontService);
font.setFont('cairo'); // ibm-plex | noto-sans | cairo | tajawal | almarai | changa | readex-pro | amiri
```

## Brand themes

Switch the primary color ramp with `DgaBrandService` (sets `data-brand` on `<html>`). Independent of light/dark.

```ts
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):

```html
<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:

```ts
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 list
```

Direction always comes from the registry (`dir`), never from “is Arabic?”. Localized labels should be maps keyed by code:

```ts
{ ar: '…', en: '…', ur: '…' }  // missing keys fall back to en, then ar
```

Load **IBM Plex Sans Arabic** (400–700) on the critical path. `DgaFontService` injects other curated families when the user switches (`prefs-boot.js` does the same before paint for a stored choice):

```html
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@400;500;600;700&display=swap"
  rel="stylesheet"
/>
```

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` &lt; 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](https://design.dga.gov.sa/guidelines/foundations/layout-and-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

```ts
import { DgaThemeService } from '@a.nemreen/dga-tokens';

inject(DgaThemeService).toggle();
```

## License

MIT — see [LICENSE](./LICENSE). Copyright (c) 2026 Ahmed Nemreen.

Use it in any project. **Do not rebrand and sell it as your own unique product.**
