| Status | Documentation | Test coverage | Features | Npm version | 
|---|---|---|---|---|
| 🟢 99.3% | 🧪 English 🏴 Українською 🇺🇦 | 🟢 97.9% | ✅ d.ts 📜 system.md 🕹️ playground | — | 
A library for creating framework-agnostic UI elements. Allows describing interfaces as simple objects.
The @nan0web/ui-core package provides a minimal yet powerful foundation for
framework-agnostic UI elements. It is designed to enable developers to describe
UI components as declarative objects. Core classes and utilities:
- Element— the main class for representing UI elements.
- processI18n— utility for translating and substituting variables in content.
- tokens— design system tokens for consistent UI styling.
- Theme— base class for creating and organizing UI themes.
- getUserTheme— function to select or create a custom theme.
These are perfect for building design systems, themeable UI frameworks, and reusable component libraries.
How to install with npm?
npm install @nan0web/ui-coreHow to install with pnpm?
pnpm add @nan0web/ui-coreHow to install with yarn?
yarn add @nan0web/ui-coreElements can be created with a component type, content, and props.
How to create a basic Element instance from object?
import Element from '@nan0web/ui-core'
const element = new Element({
	Button: ["Click me"],
	$variant: "primary"
})
console.info(element.type)    // "Button"
console.info(element.content) // ["Click me"]
console.info(element.props)   // { variant: "primary" }Elements can include child elements as arrays.
How to create nested Elements and check hasChildren()?
import Element from '@nan0web/ui-core'
const element = new Element({
	div: [
		{ span: "Hello World" },
		{ Button: ["Submit"] }
	],
	$style: "color: blue; margin: 10px"
})
console.info(element.hasChildren()) // true
console.info(element.getChildElements().length) // 2Element handles $aria* props and converts them to aria-* attributes.
How to parse aria attributes like $ariaLabel?
import Element from '@nan0web/ui-core'
const element = new Element({
	button: "Close",
	$ariaLabel: "Close dialog"
})
console.info(element.props) // { "aria-label": "Close dialog" }Element recognizes $on* props as event handlers.
How to parse event handlers like $onClick and $onKeyDown?
import Element from '@nan0web/ui-core'
const handleClick = () => console.log("Clicked")
const element = new Element({
	button: "Click me",
	$onClick: handleClick,
	$onKeyDown: () => {}
})
console.info(typeof element.props.onClick)
console.info(typeof element.props.onKeyDown)Elements support translations and variable substitution via processI18n.
How to process translations with processI18n and $t?
import { processI18n } from '@nan0web/ui-core'
const input = { $t: "greetings.hello" }
const t = (key) => key === "greetings.hello" ? "Привіт!" : key
const result = processI18n(input, t)
console.info(result) // "Привіт!"How to substitute variables in text content with processI18n?
import { processI18n } from '@nan0web/ui-core'
const text = "User: {{name}}, Age: {{age}}"
const data = { name: "Іван", age: "30" }
const result = processI18n(text, null, data)
console.info(result) // "User: Іван, Age: 30"There is a CLI sandbox to experiment safely:
git clone https://github.com/nan0web/ui-core.git
cd ui-core
npm install
npm run playflowchart TD
    A[Structure] -->|UI-Block| B(Element)
    C[Styling] -->|Tokens + Theme| B
    D[Localization] -->|processI18n| B
    B --> E[Render в React/Vue/etc]
    style A fill:#eef,stroke:#333,color:#000
    style C fill:#efe,stroke:#333,color:#000
    style D fill:#fee,stroke:#333,color:#000
    style B fill:#cfc,stroke:#333,color:#000
    style E fill:#ffcc00,stroke:#333,color:#000
    - 
Properties - type– the component type or HTML tag (e.g. "Button", "div").
- content– the element content (text, array, or nested elements).
- props– the extracted props with expanded $-keys.
 
- 
Methods - hasChildren()– returns true if element contains nested elements.
- hasText()– returns true if content includes text.
- getChildElements()– returns an array of child Element instances.
- static from(input)– creates or returns existing Element.
 
- 
Static Properties - PROP_ALIASES– map of custom key aliases (e.g.,- $classfor- className).
 
- 
Static Methods - parseProp()– transforms a $-prop into standard form.
- parseInlineStyle()– turns CSS string into style object.
- extractProps()– pulls all $-prefixed props.
- extractTags()– pulls non-prefixed keys as [type, content].
 
flowchart TD
    I["Input: { Button, $props, content }"] --> J["Element.from(input)"]
    J --> K[extractProps + extractTags]
    K --> L[parseProp: $onClick → onClick]
    K --> M[parseInlineStyle: 'color:red']
    K --> N[PROP_ALIASES: $variant → variant]
    N --> O[Element Instance]
    O --> P[hasChildren?]
    O --> Q[hasText?]
    O --> R["getChildElements()"]
    style I fill:#eef,stroke:#333,color:#000
    style O fill:#cfc,stroke:#333,color:#000
    style K fill:#def,stroke:#333,color:#000
    How to create Element with complex content?
import Element from '@nan0web/ui-core'
const element = new Element({
	div: [
		"Text content",
		{ span: "Nested element" }
	],
	$className: "container",
	$onClick: () => {}
})
console.info(element.type)         // "div"
console.info(element.hasText())    // true
console.info(element.hasChildren()) // true- 
Parameters - input– content to translate or substitute.
- t– optional translation function.
- data– optional variable substitution object.
 
- 
Returns - processed content (string, array, or unchanged type).
 
How to use processI18n with arrays?
import { processI18n } from '@nan0web/ui-core'
const content = [
	"Name: {{name}}",
	{ $t: "welcome" },
	[{ $t: "nested" }]
]
const t = (key) => key === "welcome" ? "Welcome" : "Nested"
const data = { name: "John" }
const result = processI18n(content, t, data)
console.info(result) // ["Name: John", "Welcome", ["Nested"]]Themes are built using atoms, molecules, and organisms.
flowchart TD
    T[tokens.js] --> U["space, color, radius, shadow"]
    U --> V[Theme]
    V --> W[atoms: Button, Input]
    V --> X[molecules: Card]
    V --> Y[organisms: Modal]
    V --> Z[CustomTheme]
    V --> AA[DarkLightTheme]
    V --> AB[NightTheme]
    Z --> AC["getUserTheme(config)"]
    AA --> AD["getActiveTheme() → prefers-color-scheme"]
    style T fill:#efe,stroke:#333,color:#000
    style V fill:#cfc,stroke:#333,color:#000
    style Z fill:#ddf,stroke:#333,color:#000
    style AA fill:#ddf,stroke:#333,color:#000
    How to access theme tokens?
import { tokens } from '@nan0web/ui-core'
console.info(tokens.color.primary) // "var(--color-primary)"
console.info(tokens.font.size.base) // "1rem"Create a custom theme using getUserTheme.
How to create a custom theme using getUserTheme?
import { getUserTheme } from '@nan0web/ui-core'
const theme = getUserTheme({
	atoms: { Button: { background: "red" } },
	color: { background: "#fff" }
})
console.info(theme.atoms.Button.background) // "red"Automatically switches between dark/light themes.
How to check active theme in DarkLightTheme?
import { DarkLightTheme } from '@nan0web/ui-core'
DarkLightTheme.current = "light"
const theme = DarkLightTheme.getActiveTheme()
console.info(theme) // { background: "#fff", text: "#000" }Uses d.ts files for autocompletion
How to contribute? - check here
How to license ISC? - check here