On this page
frontend utilities
introduction
dframework ships a complete utility layer for styling and dom interaction. utility classes are generated at compile time. no runtime css in js. frontend functions are available globally in every page script and component.
button classes
you can use prestyled buttons by using the base class btn and then chaining a prefix class btn-* where * represents the button color.
| available colors | description |
|---|---|
.btn-accent |
accent colored button |
.btn-red |
soft red button |
.btn-yellow |
soft yellow button |
.btn-green |
soft green button |
.btn-blue |
soft blue button |
.btn-purple |
soft purple button |
.btn-pink |
soft pink button |
.btn-gray |
var(--container) colored button |
.btn-transparent |
transparent button (use for animation) |
responsive prefixes
any utility class can be prefixed with a viewport breakpoint. prefixed classes apply only when the viewport meets or exceeds the defined width.
| prefix | min-width |
|---|---|
sm: |
480px |
md: |
768px |
lg: |
1024px |
xl: |
1280px |
xxl: |
1536px |
ultra: |
1920px |
1<div class="flex-column md:flex-row g-1 md:g-2">...</div>
layout
positioning
| class | description |
|---|---|
.relative |
position: relative |
.fixed |
position: fixed |
.sticky |
position: sticky |
.absolute |
position: absolute |
.absolute-center |
absolute center via translate |
.absolute-x-center |
absolute horizontal center |
.absolute-y-center |
absolute vertical center |
.absolute-fill |
fill parent element absolutely |
.top-0 / .bottom-0 |
top: 0 / bottom: 0 |
.left-0 / .right-0 |
left: 0 / right: 0 |
.top-50 / .bottom-50 |
top: 50% / bottom: 50% |
.left-50 / .right-50 |
left: 50% / right: 50% |
.top-[1–10] |
top in em steps |
.bottom-[1–10] |
bottom in em steps |
.left-[1–10] |
left in em steps |
.right-[1–10] |
right in em steps |
.inset-0 |
top/right/bottom/left: 0 (no position set) |
.z-[0–auto] |
z-index: 0, 1, 2, 3, 4, 5, 10, 50, 100, auto |
grid
| class | description |
|---|---|
.grid-center |
centered grid |
.grid-2 |
2-column grid |
.grid-3 |
3-column grid |
.grid-4 |
4-column grid |
.grid-auto |
dense auto-flow grid |
.col-span-[1–4,full] |
grid column span |
.row-span-[1-3,full] |
grid row span |
.col-start-[1–4] |
grid column start |
.col-end-[1–4] |
grid column end |
.row-start-[1–3] |
grid row start |
.row-end-[1–3] |
grid row end |
flexbox
| class | description |
|---|---|
.flex-row |
flex-direction: row |
.flex-column |
flex-direction: column |
.flex-center |
center on both axes |
.flex-wrap |
flex-wrap: wrap |
.flex-1 |
flex: 1 1 0% |
.flex-auto |
flex: 1 1 auto |
.flex-none |
flex: none |
.grow / .grow-0 |
individual flex-grow control |
.shrink / .shrink-0 |
individual flex-shrink control |
.order-[1–5] / `.order-[first |
last |
| `.justify-[start | end |
| `.align-[start | end |
| `.self-[start | end |
spacing
values available for all spacing classes: 0, 025, 05, 075, 1, 125, 15, 175, 2 through 10 (integer steps). margin also accepts auto.
| prefix | applies to |
|---|---|
.p- |
all padding |
.px- |
horizontal padding |
.py- |
vertical padding |
.pt- / .pb- |
top / bottom padding |
.pl- / .pr- |
left / right padding |
.m- |
all margin |
.mx- |
horizontal margin |
.my- |
vertical margin |
.mt- / .mb- |
top / bottom margin |
.ml- / .mr- |
left / right margin |
.g- |
gap (both axes) |
.gx- |
column gap only |
.gy- |
row gap only |
sizing
| prefix | values |
|---|---|
.w- |
100vw, 5–100 (5 step), auto, min, max, fit |
.h- |
100svh, 5–100 (5 step), auto, min, max, fit |
.min-w- / .max-w- |
same as .w- |
.min-h- / .max-h- |
same as .h- |
typography
| class | description |
|---|---|
.fs-[01–10] |
font-size from .1em to 10em in .1em steps |
.fw-[100–900] |
font-weight in hundreds (100 = thin, 900 = black) |
.thin |
font-weight: 300 |
.bold |
font-weight: 700 |
.uppercase / .lowercase / .capitalize |
text-transform |
.italic / .not-italic |
font-style control |
| `.text-[left | center |
.text-vertical |
vertical writing mode |
.lh-[1–3] |
line height (1, 1.1, 1.15, 1.2, 1.25, … , 3) |
.underline / .line-through / .no-underline |
text decoration |
| `.decoration-[dotted | dashed |
| `.underline-offset-[1 | 2]` |
.truncate |
truncate with ellipsis (alias) |
.overflow-ellipsis |
truncate with ellipsis |
.line-clamp-[1–4] |
clamp to N lines with ellipsis |
.indent-[1–3] |
text-indent in em |
| `.whitespace-[pre | pre-wrap |
.break-words / .break-all / .break-keep |
word-breaking |
appearance
| class | description |
|---|---|
.bdr-[0–2] |
border-radius in .1em steps |
.bdr-circle |
border-radius: 50% |
| `.border-[s | m |
| `.border-[top | bottom |
.bg-[color] |
background (container, accent, red, blue, green, …) |
.text-[color] |
text color (same values) |
| `.shadow-[s | m |
.opacity-[0–1] |
opacity (0, .1, .2 … 1) |
.invert |
filter: invert(1) |
.grayscale / .grayscale-0 |
full / remove grayscale filter |
| `.blur-[sm | md |
| `.brightness-[50 | 75 |
| `.contrast-[50 | 75 |
| `.saturate-[0 | 50 |
.sepia |
sepia filter |
| `.hue-rotate-[15 | 30 |
.filter-none |
remove all filters |
| `.backdrop-blur-[sm | md |
| `.object-[cover | contain |
| `.object-[center | top |
.visible / .invisible |
visibility (preserves layout space) |
.list-none / .list-disc / .list-decimal |
list style type |
.list-inside / .list-outside |
list style position |
.divide-y / .divide-x |
border between children |
.table |
styled table (header, rows, hover) |
.table-bordered |
.table variant with borders |
.table-striped |
.table variant with alternating rows |
.table-compact |
.table variant with compact padding |
.table-auto / .table-fixed |
table layout mode |
.border-collapse / .border-separate |
border collapse mode |
.border-spacing-0 |
remove border-spacing |
.table-caption |
table caption styling |
.outline-none |
outline: none |
interaction
| class | description |
|---|---|
.pointer |
cursor: pointer |
.no-events |
pointer-events: none |
| `.click-haptic-[small | med]` |
.hover:bg-[color] |
background change on hover |
.hover:text-[color] |
text color change on hover |
.shadow-hover-[s,m,l] |
shadow change on hover |
.select-none / .select-text / .select-all |
user-select control |
.appearance-none |
remove native form styling |
.sr-only |
visually hidden but accessible |
.float-left / .float-right / .float-none |
float |
.clear-both / .clear-left / .clear-right / .clear-none |
clear |
.align-top / .align-middle / .align-bottom |
vertical-align |
.align-text-top / .align-text-bottom |
vertical-align (text-relative) |
.align-sub / .align-super |
vertical-align for sub/superscript |
transitions & animation
| class | description |
|---|---|
.tr-[01–1] |
transition duration from .1s to 1s |
.fade-[in,out] |
opacity transition |
.slide-in-[left,right,up,down] |
entry movement |
.slide-out-[left,right,up,down] |
exit movement |
.preload |
disables all transitions |
performance
these classes communicate rendering hints to the browser. apply them to elements with complex, frequently updated content.
| class | description |
|---|---|
.stable |
will-change: transform; contain: paint |
.self-contained |
contain: layout paint |
.isolated |
contain: strict |
.gpu |
transform: translateZ(0); will-change: transform, opacity |
dom functions
global functions available in all page scripts and components. these should be preferred over native browser apis. they return cleanup functions and integrate with dComponent's auto cleanup system.
selection
1select('.btn-primary') // querySelector shorthand2selectAll('.card') // querySelectorAll, returns a true array3select('.input', formElement) // scoped to a parent elementevents
1const cleanup = listen(button, 'click', handler);2cleanup(); // detach manually if needed3 4listenAll(selectAll('.btn'), 'click', handler);classes
1addClass('active', el)2removeClass('hidden', el)3toggleClass('open', el, state) // state is optional4hasClass('disabled', el) // returns boolean
fragments and mounting
1// parse html into a cached template fragment2const frag = createFragment('<div class="card"><span>title</span></div>');3 4// batch append multiple nodes, fragments, or html strings in a single dom operation5appendMany(container, frag, '<div class="item">text</div>', customNode);timing
1await nextFrame(); // resolves after 2 raf cycles (safe for measuring after dom changes)2await sleep(300); // promise based delay
data functions
escaping
1escapeHtml(v) // escapes html entities (&, <, >, ", ')2escapeJs(v) // escapes quotes, backticks, backslashes, dollar signs, control chars, and </script> tagstype checking
1isString(v) isNumber(v) isBoolean(v)2isArray(v) isObject(v) isFunction(v)3isEmpty(v) // true for "", [], {}, null, undefined4hasValue(v) // true if not empty, null, or undefinedarray utilities
1move(input, from, to) // move an item within an array or string2remove(input, index) // remove an item at index3replace(input, index, val) // replace an item at index4limit(input, max) // slice to a maximum length5uniquify(input) // remove duplicates from an array or string6 7asyncForEach(arr, fn) // serial async iteration (awaits each fn before continuing)extras
1random(1, 100) // inclusive integer random2uniqueId(8) // collision resistant unique id string3clipboard('text to copy') // write to system clipboard4formatTime(seconds) // returns "mm:ss"5formatDate(date, false) // returns "YYYY-MM-DD"6formatDate(date) // returns "YYYY-MM-DD HH:mm:ss"7localizeDate(date, false) // returns "YYYY-MM-DD" in the user's timezone8localizeDate(date) // returns "YYYY-MM-DD HH:mm:ss" in the user's timezone9distance(a, b) // levenshtein or jaccard distance between two strings10 11Storage.get('key') // typed localStorage wrapper12Storage.set('key', value)13Storage.remove('key')14Storage.clear()
notify & modal
notify
displays a non blocking toast notification. auto dismisses after the timer expires.
1notify('profile updated');2notify('upload failed — please try again', 6000); // custom duration in mspass a setup callback to attach interaction to notification elements. use .listen() inside the callback for automatic cleanup when the notification closes:
1notify('<button class="undo-btn">undo</button>', 5000, (n) => {2 n.listen(n.querySelector('.undo-btn'), 'click', () => {3 undoLastAction();4 n.close();5 });6});7 8// omit the timer to control close timing from the callback9notify('<button class="undo-btn">undo</button>', (n) => {10 n.listen(n.querySelector('.undo-btn'), 'click', () => n.close());11});modal
displays a floating modal. handles its opening animation and removes itself from the dom after closing.
1modal('<h2>success</h2><p>your order has been placed.</p>');pass a setup callback for interactive modals. use .listen() for automatic cleanup:
1modal(`[object Object]>2 <h2>delete post</h2>[object Object]>3 <p>this action cannot be undone.</p>[object Object]>4 <button class="btn-danger confirm">delete</button>[object Object]>5 <button class="btn cancel">cancel</button>[object Object]>6`, (m) => {7 m.listen(m.querySelector('.confirm'), 'click', async () => {8 await fetch(`/posts/${postId}`, { method: 'DELETE' });9 m.close();10 notify('post deleted');11 });12 13 m.listen(m.querySelector('.cancel'), 'click', () => m.close());14});
