CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

punkt-components

Using Punkt design system components (React and Elements/Web Components). Covers component APIs, props, events, slots, and usage patterns for all Punkt UI components. Use when building UIs with Punkt components.

Quality

83%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide
SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Punkt Components

Skill for using Punkt design system components. Covers both React (@oslokommune/punkt-react) and Elements/Web Components (@oslokommune/punkt-elements).

For CSS classes, colors, typography, spacing, grid, and layout, see the punkt-css skill.

Getting started

All usage patterns require Punkt CSS. See the punkt-css skill for setup instructions.

React (NPM)

npm add @oslokommune/punkt-react
import { PktButton } from '@oslokommune/punkt-react';

<PktButton skin="primary" variant="icon-left" iconName="user">
  Click me
</PktButton>

Punkt components load icons, SVGs, and other resources from the CDN. If your project uses a Content Security Policy (CSP), you must configure it to allow resources from https://punkt-cdn.oslo.kommune.no/. See the CSP section below.

Punkt React components are pure React — they do not wrap web components, and the package has no runtime dependency on @oslokommune/punkt-elements. You can pick React or Elements independently; nothing forces you to ship both.

The package is tree-shakeable: bundlers only include the components you import.

Next.js (App Router)

Import components directly in Server Components. Components that only render markup (PktTable and its parts, PktHeading, PktLink, PktStepper) render on the server; the rest are client components.

If you build with webpack (the default in Next.js 15, next build --webpack in Next.js 16), add the package to optimizePackageImports. Without it, importing from a Server Component ships every Punkt component to the browser, including the ones you don't use. Turbopack (the default in Next.js 16) and the Pages Router don't need it.

// next.config.mjs
export default {
  experimental: {
    optimizePackageImports: ['@oslokommune/punkt-react'],
  },
}

Elements (NPM)

npm add @oslokommune/punkt-elements
import '@oslokommune/punkt-elements/dist/pkt-button.js';
<pkt-button skin="primary">Click me</pkt-button>

Slot content can be text or elements, and it may change after the component has rendered: from Vue (v-if, {{ text }}, v-html), from a Lit parent, or with plain DOM methods (appendChild, insertBefore, removeChild, textContent, innerHTML, innerText). No wrapper element is needed for that. Whitespace between elements shows like in plain HTML (<b>Hei</b> <i>du</i> reads "Hei du"), and whitespace at the start and end of the content is ignored. When a component holds several items, like a list, wrap them in one element (<div>, <ul>): the component then keeps track of one node instead of many, and updates are many times faster.

Elements (CDN)

No build step required. Include CSS and component scripts directly:

<link href="https://punkt-cdn.oslo.kommune.no/19/css/pkt.min.css" rel="stylesheet" />
<script src="https://punkt-cdn.oslo.kommune.no/19/elements/pkt-button.js" type="module"></script>

If using CSP, see the CSP section below.

Form field sizes

Form fields accept an inputSize prop (input-size attribute in Elements) with three values:

ValueUse
"medium"Default. Standard forms — the right choice unless you have a reason
"small"Dense interfaces such as toolbars, filter bars and table rows
"xsmall"Very tight spaces where a small field is still too tall

Supported by: checkbox, checkbox group, combobox, datepicker, radio button, radio group, searchinput, select, textarea, textinput and timepicker. Checkboxes and radio buttons without inputSize inherit the size of the surrounding group or Input Wrapper.

<PktTextinput label="Search" id="q" inputSize="small" />
<pkt-textinput label="Search" id="q" input-size="small"></pkt-textinput>

Keep the size consistent across fields that sit next to each other — mixing sizes in one form makes the fields look misaligned. Smaller fields also mean smaller touch targets, so prefer "medium" on touch-first interfaces.

Fields outside the form

A form field does not have to sit inside its <form>. Set form to the form's id and the browser submits the field with that form, wherever it is on the page:

<form id="application" onSubmit={handleSubmit} />

<PktTextinput id="name" name="name" label="Name" form="application" />
<PktRadioButton id="p1" name="priority" value="normal" label="Normal" form="application" />
<PktRadioButton id="p2" name="priority" value="express" label="Express" form="application" />
<form id="application"></form>

<pkt-textinput id="name" name="name" label="Name" form="application"></pkt-textinput>
<pkt-radiobutton id="p1" name="priority" value="normal" label="Normal" form="application"></pkt-radiobutton>
<pkt-radiobutton id="p2" name="priority" value="express" label="Express" form="application"></pkt-radiobutton>

Radio buttons linked to the same form form one group, whether they are inside it or outside it, so selecting one clears the others. Form reset works the same way.

Supported by: checkbox, combobox, datepicker, fileupload, radiobutton, select, textarea, textinput and timepicker. Button also takes form, to submit or reset a form it sits outside of.

Content Security Policy (CSP)

Punkt components load fonts, icons (SVG), and other resources from https://punkt-cdn.oslo.kommune.no/. If the application uses a Content Security Policy, the CSP must allow this origin. This applies to all setup methods (NPM and CDN).

Required CSP directives:

Content-Security-Policy:
  default-src 'self';
  font-src 'self' https://punkt-cdn.oslo.kommune.no/;
  img-src 'self' https://punkt-cdn.oslo.kommune.no/;
  script-src 'self' https://punkt-cdn.oslo.kommune.no/;
  style-src 'self' 'unsafe-inline' https://punkt-cdn.oslo.kommune.no/;
  connect-src 'self' https://punkt-cdn.oslo.kommune.no/;

Why 'unsafe-inline' in style-src? Because of the intricacies of SVG styling and certain web component features, Punkt requires 'unsafe-inline' in the style-src directive. Without it, some styles will be blocked by the browser. CSP nonces can be used as an alternative but require server-side configuration.

Important: Always instruct users to configure CSP when setting up Punkt components. Missing CSP configuration is a common cause of broken fonts, missing icons, and invisible components.

Components needing a second domain

PktHeader type="global", PktFooter / PktFooterSimple, PktHeaderMenu, and PktConsent also reach https://cdn.web.oslo.kommune.no/ — a different domain from the one above. The directives above are not enough for them:

ComponentExtra directiveWhy
Global header, footer, header menuconnect-src https://cdn.web.oslo.kommune.no/Fetches the shared header/footer payload
Consentscript-src + style-src for the same domainLoads a script and stylesheet from it
Global header search fieldform-action https://www.oslo.kommune.no/Submits a GET form to the search target

They share the endpoint, so one connect-src entry covers all three. See header.md, footer.md, header-menu.md and consent.md for details and for the server-side data workaround.

Strings

All UI text comes from a single shared catalogue and can be overridden globally, per subtree, or per component. See Strings and overrides.

Components

  1. Accordion
  2. Alert
  3. Backlink
  4. Breadcrumbs
  5. Button
  6. Card
  7. Checkbox
  8. Checkbox Group
  9. Combobox
  10. Consent
  11. Datepicker
  12. File Upload
  13. Footer
  14. Header Menu
  15. Header
  16. Icon
  17. Input Wrapper
  18. Link
  19. LinkCard
  20. Loader
  21. Messagebox
  22. Modal
  23. Progressbar
  24. Radio Button
  25. Radio Group
  26. Search Input
  27. Select
  28. Steps
  29. Switch
  30. Table
  31. Tabs
  32. Tag
  33. Text Input
  34. Textarea
  35. Timepicker
Repository

oslokommune/punkt-skills

Path

skills/punkt-components/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.