PHPackages                             helsingborg-stad/styleguide - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. helsingborg-stad/styleguide

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

helsingborg-stad/styleguide
===========================

3.0.2(yesterday)51.9k↑3178.6%5[2 issues](https://github.com/helsingborg-stad/styleguide/issues)[16 PRs](https://github.com/helsingborg-stad/styleguide/pulls)1MITTypeScriptPHP ^8.3CI passing

Since Mar 22Pushed 2w ago8 watchersCompare

[ Source](https://github.com/helsingborg-stad/styleguide)[ Packagist](https://packagist.org/packages/helsingborg-stad/styleguide)[ RSS](/packages/helsingborg-stad-styleguide/feed)WikiDiscussions main-1.x Synced today

READMEChangelog (10)Dependencies (74)Versions (2113)Used By (1)

[![Contributors](https://camo.githubusercontent.com/f8998baaf9d637983c75ff57d560340a7248bd0c0374778909474bfdbcd9e690/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f68656c73696e67626f72672d737461642f7374796c6567756964652e7376673f7374796c653d666c61742d737175617265)](https://github.com/helsingborg-stad/styleguide/graphs/contributors)[![Forks](https://camo.githubusercontent.com/dd8c85f73a80aa181f5353d185e73227f7b5d1fd0cc8ad68818446b051e49f98/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f68656c73696e67626f72672d737461642f7374796c6567756964652e7376673f7374796c653d666c61742d737175617265)](https://github.com/helsingborg-stad/styleguide/network/members)[![Stargazers](https://camo.githubusercontent.com/0f922bae7d0dbd0a85a1415cda4b3fae6e6cf44f6540e24f24d982328d6977ab/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f68656c73696e67626f72672d737461642f7374796c6567756964652e7376673f7374796c653d666c61742d737175617265)](https://github.com/helsingborg-stad/styleguide/stargazers)[![Issues](https://camo.githubusercontent.com/924225f30733ea91c3f09f1c631fdb39bd2b88db5c255a322953bdf02573c06f/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f68656c73696e67626f72672d737461642f7374796c6567756964652e7376673f7374796c653d666c61742d737175617265)](https://github.com/helsingborg-stad/styleguide/issues)[![License](https://camo.githubusercontent.com/10d4543bdc3ea470f8f1f3a65505841fcbb0332b1a52c4bb87132e523e17e974/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f68656c73696e67626f72672d737461642f7374796c6567756964652e7376673f7374796c653d666c61742d737175617265)](https://raw.githubusercontent.com/helsingborg-stad/styleguide/master/LICENSE)

 [ ![Logo](docs/images/hbg-github-logo-combo.png) ](https://github.com/helsingborg-stad/styleguide)

Styleguide
==========

[](#styleguide)

 [Report Bug](https://github.com/helsingborg-stad/styleguide/issues) · [Request Feature](https://github.com/helsingborg-stad/styleguide/issues)

Summary
-------

[](#summary)

The style guide is intended for websites within Helsingborgs stad / Municipio Websites and others who use our platform. The guide provides examples, markup and themes for our standardized components. The Helsingborg Styleguide is a flexible and minimalistic component-based framework built in the BEM standard &amp; designed around the Atomic Design principle.

Release Notes
-------------

[](#release-notes)

- [Municipio 6 release: Styleguide and design tokens](docs/municipio-6-release-article.md)

### Requirements

[](#requirements)

- NodeJS &gt;= 16
- PHP &gt;= 8
- [Composer](https://getcomposer.org/)

Installation
------------

[](#installation)

1. Clone the repo:

```
git clone git@github.com:helsingborg-stad/styleguide.git
```

2. Run the build script in the cloned project:

```
cd styleguide && php build.php
```

Development
-----------

[](#development)

1. Start by running the steps under [Installation](#installation).
2. Run webpack in watch-mode:

```
npm run watch
```

### Storybook (Blade-rendered components)

[](#storybook-blade-rendered-components)

Storybook is configured as a frontend shell while components continue to be rendered by Blade.

Run:

```
npm run storybook
```

This starts:

- Storybook UI on `http://localhost:6006`
- PHP server for Blade rendering on `http://127.0.0.1:8000`

In Storybook, open `Blade/Components` and select a component route from controls.

Optional: point Storybook to another Blade server using:

```
STORYBOOK_BLADE_BASE_URL=http://your-host:port npm run storybook:ui
```

Deployment
----------

[](#deployment)

The GitHub workflow `.github/workflows/build-and-deploy.yml` supports deployments to different stages using GitHub Environments.

### Configure deploy stages

[](#configure-deploy-stages)

1. In GitHub, go to **Settings → Environments** for this repository.
2. Create environments for the stages you want to deploy to (for example `stage` and `production`).
3. Add environment **Secrets** for deploy values:

- `BACKUP_PATH`
- `DEPLOY_PATH`
- `DEPLOY_HOST`
- `DEPLOY_USER`
- `DEPLOY_KEY`
- `DEPLOY_PORT`

4. Set environment-specific secret values (`stage` and `production`) so each environment can use separate SSH host/user/port and deploy paths.

### Run deployments

[](#run-deployments)

- **Automatic deploys by branch:**
    - push to `stage` deploys to `stage`
    - push to `master` deploys to `production`
- **Stage or production (manual):** run the workflow **Build and deploy styleguide** from the Actions tab and select `deploy_stage` (`stage` or `production`).

Deploy stage controls which GitHub Environment secrets are used (`stage` branch -&gt; `stage`, `master` branch -&gt; `production`, or selected `deploy_stage` for manual runs), so stage and production can use separate SSH configs and deploy paths.

Design Tokens System
--------------------

[](#design-tokens-system)

This project uses design tokens as the single source of truth for visual values (spacing, radius, typography, color, shadow). The token system is built to keep component styles consistent while still allowing controlled overrides in consuming implementations.

### Goal

[](#goal)

- Keep design decisions centralized and reusable.
- Avoid hardcoded one-off values in component styles.
- Make components themeable at runtime through CSS custom properties.
- Keep the API stable and explicit through JSON schemas and component token declarations.

### Architecture (how it works)

[](#architecture-how-it-works)

1. **Global tokens source**: `source/data/design-tokens.json`
    - Defines categories and CSS variable defaults.
2. **Generated Sass output**: `source/sass/setting/_design-tokens.scss`
    - Auto-generated from the JSON file by:

    ```
    npm run tokens
    ```
3. **Component token declaration**: `source/components//component.json`

- Each component lists which global tokens it is allowed to consume in its `tokens` array.
- Vite still supports the legacy `source/data/c-*.json` lookup as a fallback, but new component work should use the component-local `component.json` file.

4. **Build-time token injection**: `vite.config.mjs`

- Exposes a custom Sass function `getComponentTokens($name)` that reads token arrays from `source/components//component.json`.

5. **Component-scoped token mapping**: `source/sass/mixin/_tokens.scss`
    - `@include tokens.create(...)` maps global tokens (`--color--surface`) to component-scoped tokens (`--c-card--color--surface`).
    - Components then consume these values with `tokens.getRawValue(...)`, `tokens.getCalculatedValue(...)`, or `tokens.getDerivedAliasValue(...)` for derived component aliases.

### Token Helper Functions

[](#token-helper-functions)

- `tokens.getRawValue($prefix, $token)`
    - Reads a raw component token such as color, font family, or border token.
- `tokens.getRawValue($prefix: $_, $token: "color--surface", $inheritVariable: "color-background")`
    - Use this when a real token should resolve as explicit component override -&gt; inherit hook -&gt; token default.
- `tokens.getCalculatedValue($prefix, $token, $multiplier)`
    - Reads a scale-based numeric token and applies the multiplier against `var(--base)`.
- `tokens.getCalculatedValue($prefix: $_, $token: "space", $multiplier: 2, $inheritVariable: "space")`
    - Use this when a scale-based token also needs inherit precedence.
- `tokens.getDefaultRawValue($prefix, $token)`
    - Reads the `-default` token variable emitted by `tokens.create(..., $emitDefaultTokenVariables: true)`.
- `tokens.getDerivedAliasValue($prefix, $variable, $inheritVariable)`
    - Use this for derived component aliases such as `--c-typography--h2-font-size`, where precedence must be explicit alias override -&gt; inherit hook -&gt; alias default.

### Intended Usage

[](#intended-usage)

#### 1) Define or update global tokens

[](#1-define-or-update-global-tokens)

Edit `source/data/design-tokens.json` and run:

```
npm run tokens
```

This regenerates `source/sass/setting/_design-tokens.scss`.

Example (global token):

```
{
  "id": "radius",
  "label": "Radius",
  "settings": [
    {
      "variable": "--border-radius",
      "label": "Border Radius",
      "type": "range",
      "default": 1,
      "min": 0,
      "max": 5,
      "step": 0.25
    }
  ]
}
```

#### 2) Declare which tokens a component may consume

[](#2-declare-which-tokens-a-component-may-consume)

Example in `source/components/button/component.json`:

```
{
  "tokens": [
    "base",
    "border-radius",
    "border-width",
    "space",
    "color--primary",
    "color--surface",
    "shadow"
  ]
}
```

#### 3) Create component-local token aliases in Sass

[](#3-create-component-local-token-aliases-in-sass)

```
@use "../mixin/tokens";

$_: "c-example";

@include tokens.create($_, getComponentTokens($_));

.c-example {
  border-radius: tokens.getCalculatedValue($_, "border-radius");
  background: tokens.getRawValue($_, "color--surface");
  color: tokens.getRawValue($_, "color--surface-contrast");
  filter: tokens.getCalculatedValue($_, "shadow", 2);
  padding: tokens.getCalculatedValue($_, "space", 2);
}
```

Derived alias example:

```
@use "../mixin/tokens";

$_: "c-typography";

@include tokens.create($prefix: $_, $tokens: getComponentTokens($_), $emitDefaultTokenVariables: true);

:root {
  --#{$_}--font-size-lead-default: #{tokens.getRawValue($_, "font-size-200")};
}

.#{$_}__variant--lead {
  font-size: tokens.getDerivedAliasValue($_, "font-size-lead", "font-size");
}
```

#### 4) Override tokens in implementation (theme or local instance)

[](#4-override-tokens-in-implementation-theme-or-local-instance)

Global theme override:

```
:root {
  --color--primary: #0052cc;
  --color--primary-contrast: #ffffff;
  --border-radius: 0.75;
}
```

Component instance override:

```
.c-card--campaign {
  --c-card--color--surface: #111827;
  --c-card--color--surface-contrast: #f9fafb;
  --c-card--border-radius: 1.5;
}
```

Runtime override with JavaScript:

```
const card = document.querySelector('.c-card');
card?.style.setProperty('--c-card--color--surface', '#1f2937');
```

### Architectural Flexibility

[](#architectural-flexibility)

- **Scoped customization**: The same component can have different appearances by overriding `--c---*` per instance.
- **Global theming**: Change `:root` token values to update all components consistently.
- **Explicit token mapping only** in `tokens.create(...)`:
    - Every consumed token must be listed in the component token manifest.
    - Companion tokens such as `-border` and `-alt` must be declared in global tokens and referenced explicitly.
    - Shadow internals (`shadow-color`, `shadow-amount`) must be listed directly.

### Architectural Limitations (important)

[](#architectural-limitations-important)

- **Do not edit generated token Sass directly**:
    - `source/sass/setting/_design-tokens.scss` is generated and overwritten.
- **Allowed token names are schema-driven**:
    - Component token arrays are validated against `source/design-tokens-schema.json` enum values.
- **Component must declare tokens it consumes**:
    - If a token is missing from `source/components//component.json`, it will not be mapped by `tokens.create(...)`.
- **`tokens.getCalculatedValue(...)` assumes scale-based numeric usage**:
    - It returns `calc(var(--c-...--token) * var(--base) * multiplier)` (except special cases like `base` and `shadow`).
    - For raw values or non-scale tokens, use `tokens.getRawValue(...)`.
- **Use `tokens.getDerivedAliasValue(...)` for derived component aliases**:
    - This applies when the runtime variable is not itself a token, but a component alias such as `--c-typography--h2-font-size`.
    - Keep the `-default` alias variable token-only, and apply inherit precedence at the usage site.
- **No implicit companion generation**:
    - Token behavior is declarative; add companion tokens explicitly in token JSON and component manifests.

### Recommended Workflow

[](#recommended-workflow)

1. Add/update token definitions in `source/data/design-tokens.json`.
2. Run `npm run tokens`.
3. Add token usage list in `source/components/your-component/component.json`.
4. Consume via `@include tokens.create($_, getComponentTokens($_));` in component Sass.
5. Use `tokens.getRawValue(...)` and `tokens.getCalculatedValue(...)` in styles.
6. Validate by running watch/build and checking component previews.

### Global Token Reference

[](#global-token-reference)

The base token surface lives in `source/data/design-tokens.json`. These are the system-level tokens that components consume through `tokens.create(...)`.

In the tables below, `Can the user manipulate it?` means the supported direct customization path in the token source and design-token UI. Tokens marked `No` are locked or derived tokens and should normally be changed indirectly by editing their upstream inputs. They can still be overridden in raw CSS if absolutely necessary, but that is not the intended primary workflow.

#### Base

[](#base)

TokenPurposeHow it is derivedCan the user manipulate it?`--base`Foundation unit for spacing, size, and radius calculations across the system.Defaults to `calc(1rem/2)`. Locked because it is a system baseline rather than a regular theme knob.No, not as a primary UI/token-source control. Change indirectly through root sizing or raw CSS only if the whole system baseline must move.`--base-font-size`Root type size that drives the type scale.Defaults to `16px` with a `18px` option. All computed font-size tokens derive from it.Yes.#### Layout

[](#layout)

TokenPurposeHow it is derivedCan the user manipulate it?`--container-width-multiplier`Controls the main content width scale.Direct numeric token. Default `160`.Yes.`--container-width`Final container width token used by layouts.Derived as `calc(var(--base) * var(--container-width-multiplier))`.No. Change `--container-width-multiplier` or `--base` instead.`--container-width-wide-multiplier`Controls how much wider the wide container is than the default container.Direct numeric token. Default `1.25`.Yes.`--container-width-wide`Final wide-container width token.Derived as `calc(var(--container-width) * var(--container-width-wide-multiplier))`.No. Change `--container-width-wide-multiplier` or upstream container tokens instead.#### Radius

[](#radius)

TokenPurposeHow it is derivedCan the user manipulate it?`--border-radius`Radius scale input used by components for rounded corners.Direct numeric token. Default `1`. Components usually multiply it by `--base`.Yes.`--corner-shape`Controls the corner rendering mode where `corner-shape` is used.Direct select token. Default `round`.Yes.#### Typography

[](#typography)

TokenPurposeHow it is derivedCan the user manipulate it?`--font-family-base`Default body font family.Direct font token. Default `"Roboto", sans-serif`.Yes.`--font-family-heading`Heading font family.Defaults to `var(--font-family-base)`, so it inherits the body font unless explicitly changed.Yes.`--font-family-code`Monospace/code font family.Direct token with a fixed default stack. Marked locked.No, not through the intended token UI flow.`--font-size-scale-ratio`Ratio used to generate the full font-size scale.Direct select token. Default `1.250`.Yes.`--font-weight-normal`Default text weight.Direct select token. Default `400`.Yes.`--font-weight-medium`Medium emphasis font weight.Direct select token. Default `500`.Yes.`--font-weight-bold`Strong emphasis font weight.Direct select token. Default `700`.Yes.`--font-weight-heading`Default heading weight.Direct select token. Default `700`.Yes.`--line-height-base`Default body line height.Direct numeric token. Default `1.625`.Yes.`--line-height-heading`Default heading line height.Direct numeric token. Default `1.33`.Yes.`--letter-spacing-base`Default text letter spacing.Direct numeric token. Default `0em`.Yes.#### Font Sizes

[](#font-sizes)

TokenPurposeHow it is derivedCan the user manipulate it?`--font-size-80`Two steps below the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), -2))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-90`One step below the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), -1))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-100`Base type size token.Derived as `var(--base-font-size)`.No. Change `--base-font-size` instead.`--font-size-200`One step above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 1))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-300`Two steps above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 2))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-400`Three steps above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 3))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-500`Four steps above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 4))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-600`Five steps above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 5))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-700`Six steps above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 6))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.`--font-size-800`Seven steps above the base type size.Derived as `calc(var(--base-font-size) * pow(var(--font-size-scale-ratio), 7))`.No. Change `--base-font-size` or `--font-size-scale-ratio` instead.#### Borders

[](#borders)

TokenPurposeHow it is derivedCan the user manipulate it?`--border-width`Base border width token for UI elements.Direct numeric token. Default `0.125`.Yes.`--color--border-mix-amount`Mix ratio used when generating derived `*-border` companion colors.Direct percentage token. Default `10%`.Yes.#### Spacing

[](#spacing)

TokenPurposeHow it is derivedCan the user manipulate it?`--space`Standard internal spacing token for paddings and margins inside components.Direct numeric token. Default `1`.Yes.`--outer-space`Spacing token for gaps between components or outer layout rhythm.Direct numeric token. Default `1`.Yes.#### Shadows

[](#shadows)

TokenPurposeHow it is derivedCan the user manipulate it?`--shadow-color`Base color used by shadow formulas.Direct RGBA token. Default `rgba(0, 0, 0, 0.1)`.Yes.`--shadow-amount`Global multiplier for shadow intensity.Direct numeric token. Default `0.7`. Components typically combine it with `--base` in shadow calculations.Yes.#### Brand Colors

[](#brand-colors)

TokenPurposeHow it is derivedCan the user manipulate it?`--color--primary`Main brand/action color.Direct color token. Default `#2d2d2d`.Yes.`--color--primary-contrast`Text/icon color on top of primary backgrounds.Direct color token. Default `#ffffff`.Yes.`--color--primary-border`Border/emphasis companion for primary surfaces.Derived as `color-mix(in srgb, var(--color--primary-contrast) var(--color--border-mix-amount), var(--color--primary))`.No. Change `--color--primary`, `--color--primary-contrast`, or `--color--border-mix-amount` instead.`--color--primary-alt`Softer alternate primary surface.Derived as `color-mix(in srgb, var(--color--primary-contrast) var(--color--alt-mix-amount), var(--color--primary))`.No. Change `--color--primary`, `--color--primary-contrast`, or `--color--alt-mix-amount` instead.`--color--secondary`Secondary brand color.Direct color token. Default `#6e6e6e`.Yes.`--color--secondary-contrast`Text/icon color on top of secondary backgrounds.Direct color token. Default `#ffffff`.Yes.`--color--secondary-border`Border/emphasis companion for secondary surfaces.Derived as `color-mix(in srgb, var(--color--secondary-contrast) var(--color--border-mix-amount), var(--color--secondary))`.No. Change `--color--secondary`, `--color--secondary-contrast`, or `--color--border-mix-amount` instead.`--color--secondary-alt`Softer alternate secondary surface.Derived as `color-mix(in srgb, var(--color--secondary-contrast) var(--color--alt-mix-amount), var(--color--secondary))`.No. Change `--color--secondary`, `--color--secondary-contrast`, or `--color--alt-mix-amount` instead.#### Layout Colors

[](#layout-colors)

TokenPurposeHow it is derivedCan the user manipulate it?`--color--alt-mix-amount`Mix ratio used when generating `*-alt` surface colors.Direct percentage token. Default `4%`.Yes.`--color--background`Main page/application background color.Direct color token. Default `#f5f5f5`.Yes.`--color--background-contrast`Foreground color for content on background surfaces.Direct color token. Default `#2d2d2d`.Yes.`--color--background-contrast-muted`Muted foreground for background surfaces.Derived as `color-mix(in srgb, var(--color--background-contrast) 67.5%, var(--color--background))`.No. Change `--color--background` or `--color--background-contrast` instead.`--color--background-border`Border/emphasis companion for background surfaces.Derived as `color-mix(in srgb, var(--color--background-contrast) var(--color--border-mix-amount), var(--color--background))`.No. Change `--color--background`, `--color--background-contrast`, or `--color--border-mix-amount` instead.`--color--surface`Default elevated/content surface color.Direct color token. Default `#ffffff`.Yes.`--color--surface-contrast`Foreground color for content on surface backgrounds.Direct color token. Default `#2d2d2d`.Yes.`--color--surface-contrast-muted`Muted foreground for surface backgrounds.Derived as `color-mix(in srgb, var(--color--surface-contrast) 67.5%, var(--color--surface))`.No. Change `--color--surface` or `--color--surface-contrast` instead.`--color--surface-border`Border/emphasis companion for surface elements.Derived as `color-mix(in srgb, var(--color--surface-contrast) var(--color--border-mix-amount), var(--color--surface))`.No. Change `--color--surface`, `--color--surface-contrast`, or `--color--border-mix-amount` instead.`--color--surface-alt`Softer alternate surface color.Derived as `color-mix(in srgb, var(--color--surface-contrast-muted) var(--color--alt-mix-amount), var(--color--surface))`.No. Change `--color--surface`, `--color--surface-contrast`, or `--color--alt-mix-amount` instead.#### UI Colors

[](#ui-colors)

TokenPurposeHow it is derivedCan the user manipulate it?`--color--focus`Focus ring and focus state color.Direct color token. Default `#4d90fe`.Yes.`--color--alpha`Overlay color including opacity.Direct RGBA token. Default `rgba(0, 0, 0, 0.1)`.Yes.`--color--alpha-contrast`Foreground color on top of alpha overlays.Direct color token. Default `#ffffff`.Yes.`--color--alpha-border`Border/emphasis companion for alpha overlays.Derived as `color-mix(in srgb, var(--color--alpha-contrast) var(--color--border-mix-amount), var(--color--alpha))`.No. Change `--color--alpha`, `--color--alpha-contrast`, or `--color--border-mix-amount` instead.#### State Colors

[](#state-colors)

TokenPurposeHow it is derivedCan the user manipulate it?`--color--success`Success state background/accent color.Direct color token. Default `#4caf50`.Yes.`--color--success-contrast`Foreground color for success surfaces.Direct color token. Default `#ffffff`.Yes.`--color--success-border`Border/emphasis companion for success surfaces.Derived as `color-mix(in srgb, var(--color--success-contrast) var(--color--border-mix-amount), var(--color--success))`.No. Change `--color--success`, `--color--success-contrast`, or `--color--border-mix-amount` instead.`--color--warning`Warning state background/accent color.Direct color token. Default `#ffb300`.Yes.`--color--warning-contrast`Foreground color for warning surfaces.Direct color token. Default `#2d2d2d`.Yes.`--color--warning-border`Border/emphasis companion for warning surfaces.Derived as `color-mix(in srgb, var(--color--warning-contrast) var(--color--border-mix-amount), var(--color--warning))`.No. Change `--color--warning`, `--color--warning-contrast`, or `--color--border-mix-amount` instead.`--color--danger`Danger/error state background/accent color.Direct color token. Default `#e53935`.Yes.`--color--danger-contrast`Foreground color for danger surfaces.Direct color token. Default `#ffffff`.Yes.`--color--danger-border`Border/emphasis companion for danger surfaces.Derived as `color-mix(in srgb, var(--color--danger-contrast) var(--color--border-mix-amount), var(--color--danger))`.No. Change `--color--danger`, `--color--danger-contrast`, or `--color--border-mix-amount` instead.`--color--info`Informational state background/accent color.Direct color token. Default `#039be5`.Yes.`--color--info-contrast`Foreground color for info surfaces.Direct color token. Default `#ffffff`.Yes.`--color--info-border`Border/emphasis companion for info surfaces.Derived as `color-mix(in srgb, var(--color--info-contrast) var(--color--border-mix-amount), var(--color--info))`.No. Change `--color--info`, `--color--info-contrast`, or `--color--border-mix-amount` instead.### Related Files

[](#related-files)

- `source/data/design-tokens.json` (global token source)
- `build-design-tokens.mjs` (JSON -&gt; generated Sass compiler)
- `source/sass/setting/_design-tokens.scss` (generated root CSS vars)
- `source/sass/mixin/_tokens.scss` (token API for components)
- `source/components/*/component.json` (component token whitelists)
- `source/design-tokens-schema.json`, `source/component-schema.json`, and `source/utility-schema.json` (validation/contracts)

Testing
-------

[](#testing)

### Contribution Rule-Set (Do and Don't)

[](#contribution-rule-set-do-and-dont)

The rules below are aligned with the validator tests in `source/validators/Tests` and should be followed for all component and style changes.

#### Do

[](#do)

- **Do use design tokens as the source of truth** in component styles.
- **Do declare component token usage** in `source/components//component.json`.
- **Do namespace component CSS custom properties** so they remain component-scoped.
- **Do declare each `--inherit-*` variable with `@property` and `inherits: false`** when used.
- **Do keep JavaScript tests adjacent to the file under test** (`*.test.ts` / `*.test.js`).
- **Do run validator and unit tests before opening a PR**.

#### Don't

[](#dont)

- **Don't use Sass variables in component SCSS**, except the allowed component name variable `$_` in token helper calls.
- **Don't mix token/Sass variable patterns in utility files** that should rely on CSS custom properties.
- **Don't use un-namespaced CSS custom properties** in component SCSS.
- **Don't reference CSS variables that are not declared in design tokens / generated variable sources**.
- **Don't edit generated token output directly** (for example `source/sass/setting/_design-tokens.scss`), as it will be overwritten.

#### When A Component Affects Another Component

[](#when-a-component-affects-another-component)

When a component needs to affect the appearance or functionality of an inner component, only two methods are allowed.

##### 1. Inherit Variables

[](#1-inherit-variables)

Some components declare `--inherit-*` variables specifically to pass settings down to another component. These variables always take precedence over the receiving component's default values and should be the first choice when the receiving component explicitly supports them.

Example flow:

- Override: [`source/components/segment/text.scss`](./source/components/segment/text.scss#L9-L15)
- Declaration: [`source/components/typography/style.scss`](./source/components/typography/style.scss#L3-L18)
- Implementation: [`source/components/typography/style.scss`](./source/components/typography/style.scss#L81-L115)

##### 2. Direct Component Targeting

[](#2-direct-component-targeting)

Direct targeting may be used for smaller modifications, especially when the affected property is not exposed through an inherit variable. This is only allowed for outer-context adaptation on the outermost element of the inner component.

Example:

- [`source/components/block/style.scss`](./source/components/block/style.scss#L131-L138)

#### Test-Backed Rules (Source of Truth)

[](#test-backed-rules-source-of-truth)

- `source/validators/Tests/NoSassVariablesTest.php`
- `source/validators/Tests/TokenMixingTest.php`
- `source/validators/Tests/CssVariablesNamespacedTest.php`
- `source/validators/Tests/CssVariablesReferencesDesignTokensTest.php`
- `source/validators/Tests/InheritVariablesDeclaredTest.php`

#### Quick Verification Before PR

[](#quick-verification-before-pr)

```
# PHP validator tests (SCSS/token contracts)
./vendor/bin/phpunit source/validators/Tests

# JavaScript unit tests
npm test
```

Jest is used as testing framework for JavaScript in the StyleGuide.

Test files should be added adjacent to the file that is the subject fo testing. Naming convention for test files is to use the same name as the file that is subject for testing and be appended with ".test.js" or "test.ts". The ".ts" file ending enables some IDE's, like VS Code, to add intellisense for Jest.

### Example file accompanied by test file

[](#example-file-accompanied-by-test-file)

```
source/js
├── gallery.js
├── gallery.test.ts

```

### Test scripts

[](#test-scripts)

- Run tests: ```
    npm test
    ```
- Runs tests in file watch mode: ```
    npm run test:dev
    ```

VS Code Devcontainer &amp; Codespaces
-------------------------------------

[](#vs-code-devcontainer--codespaces)

Configuration for Codespaces is available and to get it up and running do the following after opening a Codespace:

1. Run task `Setup`.
2. Run task `Serve`.

License
-------

[](#license)

Distributed under the [MIT License](https://raw.githubusercontent.com/helsingborg-stad/styleguide/master/LICENSE).

Acknowledgements
----------------

[](#acknowledgements)

- [othneildrew Best README Template](https://github.com/othneildrew/Best-README-Template)

###  Health Score

65

—

FairBetter than 99% of packages

Maintenance90

Actively maintained with recent releases

Popularity29

Limited adoption so far

Community32

Small or concentrated contributor base

Maturity95

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

1878

Last Release

1d ago

Major Versions

v0.11.1403 → 2.207.72026-04-13

v0.11.1404 → 2.225.62026-04-28

v0.11.1405 → 2.258.32026-05-28

v0.11.1406 → 2.258.52026-05-29

2.281.17 → 3.0.02026-07-01

PHP version history (3 changes)v0.11.1014PHP ^8.0

2.152.3PHP ^8.2

3.0.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/f46fe64973c3e89d15c745c0bf601c25983bafea81d30e64d4bf813a6f8c8c7c?d=identicon)[sebastianthulin](/maintainers/sebastianthulin)

---

Top Contributors

[![actions-user](https://avatars.githubusercontent.com/u/65916846?v=4)](https://github.com/actions-user "actions-user (1377 commits)")[![NiclasNorin](https://avatars.githubusercontent.com/u/103985736?v=4)](https://github.com/NiclasNorin "NiclasNorin (813 commits)")[![silvergrund](https://avatars.githubusercontent.com/u/4200504?v=4)](https://github.com/silvergrund "silvergrund (722 commits)")[![alexanderbomanskoug2](https://avatars.githubusercontent.com/u/39676080?v=4)](https://github.com/alexanderbomanskoug2 "alexanderbomanskoug2 (366 commits)")[![thorbrink](https://avatars.githubusercontent.com/u/1064724?v=4)](https://github.com/thorbrink "thorbrink (235 commits)")[![ergr1001](https://avatars.githubusercontent.com/u/97021637?v=4)](https://github.com/ergr1001 "ergr1001 (202 commits)")[![cedrikvonheiroth](https://avatars.githubusercontent.com/u/64852452?v=4)](https://github.com/cedrikvonheiroth "cedrikvonheiroth (117 commits)")[![nRamstedt](https://avatars.githubusercontent.com/u/16800993?v=4)](https://github.com/nRamstedt "nRamstedt (91 commits)")[![sebastianthulin](https://avatars.githubusercontent.com/u/797129?v=4)](https://github.com/sebastianthulin "sebastianthulin (82 commits)")[![Muckbuck](https://avatars.githubusercontent.com/u/11438804?v=4)](https://github.com/Muckbuck "Muckbuck (80 commits)")[![faejr](https://avatars.githubusercontent.com/u/752642?v=4)](https://github.com/faejr "faejr (80 commits)")[![annalinneajohansson82](https://avatars.githubusercontent.com/u/232372534?v=4)](https://github.com/annalinneajohansson82 "annalinneajohansson82 (42 commits)")[![jonatanhanson](https://avatars.githubusercontent.com/u/21363149?v=4)](https://github.com/jonatanhanson "jonatanhanson (15 commits)")[![michaelclaesson](https://avatars.githubusercontent.com/u/18331514?v=4)](https://github.com/michaelclaesson "michaelclaesson (10 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (8 commits)")[![malinkytta](https://avatars.githubusercontent.com/u/113056103?v=4)](https://github.com/malinkytta "malinkytta (6 commits)")[![yo-l1982](https://avatars.githubusercontent.com/u/154269?v=4)](https://github.com/yo-l1982 "yo-l1982 (5 commits)")[![rubikuo](https://avatars.githubusercontent.com/u/46818235?v=4)](https://github.com/rubikuo "rubikuo (3 commits)")[![mikael-stromgren](https://avatars.githubusercontent.com/u/8775561?v=4)](https://github.com/mikael-stromgren "mikael-stromgren (3 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (2 commits)")

---

Tags

getmunicipiomunicipionodejsphpscss

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/helsingborg-stad-styleguide/health.svg)

```
[![Health](https://phpackages.com/badges/helsingborg-stad-styleguide/health.svg)](https://phpackages.com/packages/helsingborg-stad-styleguide)
```

###  Alternatives

[helsingborg-stad/municipio

A bootstrap theme for creating municipality sites.

4028.5k10](/packages/helsingborg-stad-municipio)[pocketmine/pocketmine-mp

A server software for Minecraft: Bedrock Edition written in PHP

3.5k78.3k90](/packages/pocketmine-pocketmine-mp)[shlinkio/shlink

A self-hosted and PHP-based URL shortener application with CLI and REST interfaces

5.1k5.2k](/packages/shlinkio-shlink)[firefly-iii/data-importer

Firefly III Data Import Tool.

8045.8k](/packages/firefly-iii-data-importer)[mynaparrot/plugnmeet-sdk

plugNmeet PHP SDK

102.8k](/packages/mynaparrot-plugnmeet-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
