@konce-pt/grid
v0.7.0
Published
Mobile-first layout system for Koncept UI — CSS Grid columns, flexbox utilities and container queries driven by custom properties. Framework-agnostic (SCSS source).
Maintainers
Readme
@konce-pt/grid
Mobile-first layout system for Koncept UI — CSS Grid columns, flexbox utilities, gap and order. Plain CSS: no framework, no runtime.
Utility classes set custom properties only. A fixed number of @media rules turns
them into a layout, so the stylesheet grows with the number of breakpoints, not
with the number of columns — and changing a span never touches a class in the DOM.
- Install:
pnpm add @konce-pt/grid - License: MIT
Setup
// Vite / webpack — order matters
import '@konce-pt/tokens/css';
import '@konce-pt/tokens/css/dark';
import '@konce-pt/styles';
import '@konce-pt/grid';With the Angular CLI put the files in angular.json → styles[] in the same order
instead of importing them in main.ts.
SCSS source: @use 'pkg:@konce-pt/grid/scss';
Quick start
<div class="kpt-grid">
<div class="kpt-col-12 kpt-col-sm-6 kpt-col-md-4">Card 1</div>
<div class="kpt-col-12 kpt-col-sm-6 kpt-col-md-4">Card 2</div>
<div class="kpt-col-12 kpt-col-md-4">Card 3</div>
</div>With Angular, @konce-pt/angular/grid adds directives with signal inputs:
<div kptGrid>
<div [kptCol]="12" [kptColSm]="6" [kptColMd]="4">Card 1</div>
</div>Breakpoints
Breakpoints are design tokens shared with the rest of the library. Mobile-first:
a value applies from its breakpoint upwards until another one overrides it, so
kpt-col-12 kpt-col-lg-5 is 12 on mobile, sm and md, and 5 only from lg.
| Token | From | Class |
| --- | --- | --- |
| — | 0 | kpt-col-6 |
| --kpt-breakpoint-sm | 640px | kpt-col-sm-6 |
| --kpt-breakpoint-md | 768px | kpt-col-md-6 |
| --kpt-breakpoint-lg | 1024px | kpt-col-lg-6 |
| --kpt-breakpoint-xl | 1280px | kpt-col-xl-6 |
| --kpt-breakpoint-2xl | 1536px | kpt-col-2xl-6 |
@media cannot read var(), so the rules need compile-time values. The build injects them
straight from the token source (scripts/gen-tokens.mjs → src/_tokens.generated.scss), which
keeps the tokens the single source of truth while leaving src/ self-contained — recompiling it
needs no resolution of bare imports into node_modules.
Classes
Every class has per-breakpoint variants in the shape kpt-<util>-<bp>-<value>:
kpt-col-md-6, kpt-justify-lg-between, kpt-gap-md-6, kpt-order-md-none.
| Group | Classes |
| --- | --- |
| Layout | kpt-grid · kpt-container · kpt-form-grid |
| Columns | kpt-col-1…12 · kpt-col-full · kpt-col-auto · kpt-col-start-1…12 |
| Display | kpt-flex · kpt-flex-inline |
| Direction | kpt-flex-row · -row-reverse · -column · -column-reverse |
| Wrap | kpt-flex-wrap · -nowrap · -wrap-reverse |
| Flex item | kpt-flex-1 · -auto · -initial · -none · kpt-grow-0/1 · kpt-shrink-0/1 |
| Justify | kpt-justify-start · -end · -center · -between · -around · -evenly |
| Align items | kpt-align-start · -end · -center · -baseline · -stretch |
| Align content | kpt-content-start · -end · -center · -between · -around · -evenly |
| Align self | kpt-self-auto · -start · -end · -center · -stretch · -baseline |
| Gap | kpt-gap-0…16 · kpt-gap-x-* · kpt-gap-y-* |
| Order | kpt-order-first · -last · -none · kpt-order-1…12 |
How many columns?
Twelve out of the box. Two ways to change that, solving different problems.
At runtime — one grid or the whole app, no recompilation:
:root { --kpt-grid-columns: 18; }<div class="kpt-grid" style="--kpt-grid-columns:18">…</div>Utility classes stop at kpt-col-12, because classes cannot be generated at
runtime. For spans above 12 set the variable inline (style="--kpt-col:15") or
use the [kptCol] directive, which does exactly that.
At build time — the whole app, with the full set of classes:
// src/styles.scss — generates kpt-col-1 … kpt-col-18 and every breakpoint variant
@use 'pkg:@konce-pt/grid/scss' with ($columns: 18);The pkg: prefix is how Sass resolves a package exports entry; the Angular CLI and Vite
understand it out of the box, and the Sass CLI needs --pkg-importer=node. Without it, point
Sass at the file and add node_modules to the load path:
@use '@konce-pt/grid/src/index' with ($columns: 18);
When you compile the source, do not also import @konce-pt/grid — both
stylesheets would end up in the bundle.
Container mode
<div class="kpt-grid" data-container>…</div>Spans follow the width of the grid itself instead of the window, so a card in a narrow sidebar stacks even when the viewport is wide.
A limitation of CSS, not of this package: @container queries the nearest
ancestor container, so an element cannot query its own. Spans, start, order and
self follow the container; column count and gaps still follow the viewport.
Form layout
<form class="kpt-grid kpt-form-grid">
<kpt-form-field label="First name">…</kpt-form-field>
<kpt-form-field label="Last name">…</kpt-form-field>
<kpt-form-field label="Notes" class="kpt-col-md-12">…</kpt-form-field>
</form>Children stack on mobile and split into two columns from md, with no classes of
their own. The preset uses :where(), so its specificity is zero — any utility
class or [kptCol] overrides it, as the notes field does above.
Cascade layers
Rules live in kpt.utilities, the highest of the declared layers
(kpt.reset < kpt.base < kpt.components < kpt.utilities). They therefore win over
component styles, while your own rules outside any layer win over them — no
specificity war, no !important.
Custom properties
| Property | Scope | Description |
| --- | --- | --- |
| --kpt-grid-columns | grid | Column count. Inherits, so it can be set once on :root. |
| --kpt-gap | grid | Gap on both axes. |
| --kpt-gap-x / --kpt-gap-y | grid | Gap on one axis — overrides --kpt-gap. |
| --kpt-justify / --kpt-align / --kpt-content | grid | Box alignment. |
| --kpt-flex-direction / --kpt-flex-wrap | grid | Flex container axis and wrapping. |
| --kpt-container-padding | grid | Horizontal padding of .kpt-container. |
| --kpt-col | column | Column span — the mobile-first base. |
| --kpt-col-start | column | Grid line the column starts on (1-based); this is how offsets work. |
| --kpt-order | column | Visual order. Does not change the DOM. |
| --kpt-self | column | align-self of a single child. |
| --kpt-flex / --kpt-grow / --kpt-shrink | column | Flex item sizing. |
Every property has per-breakpoint variants in the same shape: --kpt-col → --kpt-col-md.
Accessibility
order is visual only. Screen readers and keyboard navigation follow the DOM, so
do not use it to move interactive content — reorder the markup instead.
