@chipmo/design-system 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md ADDED
@@ -0,0 +1,43 @@
1
+ # Chipmo design system — instructions for AI coding agents
2
+
3
+ You are working in a repo that uses `@chipmo/design-system`, the shared UI library for all Chipmo ERP products. Follow these rules in every UI change.
4
+
5
+ ## Hard rules
6
+
7
+ 1. **Never hardcode colors, spacing, or fonts.** Always use the CSS variables from `@chipmo/design-system/tokens.css`. If you find yourself typing a hex value like `#E68524`, stop — use `var(--chp-primary)` instead. The full token list is in `tokens/design-tokens.json`.
8
+ 2. **Never build a button, badge, input, or card from scratch.** Use the existing components: `<chp-button>`, `<chp-badge>`, `<chp-input>`, `<chp-card>`. Check `custom-elements.json` for the full API (attributes, events, slots) of every component before using it.
9
+ 3. **Import once, per framework:**
10
+ - Plain HTML / any framework: `import '@chipmo/design-system'` registers all elements.
11
+ - React: `import { Button, Badge, Input, Card } from '@chipmo/design-system/react'`.
12
+ - Vue 3: `import '@chipmo/design-system/vue'` in main.js, then use `<chp-*>` tags directly (configure `isCustomElement: tag => tag.startsWith('chp-')`).
13
+ - Always import the tokens once at app root: `import '@chipmo/design-system/tokens.css'`.
14
+ 4. **Dark mode is automatic.** Tokens flip via `prefers-color-scheme`, or explicitly with `data-theme="dark"` / `data-theme="light"` on `<html>`. Never write your own dark-mode color overrides.
15
+ 5. **Events are prefixed.** Components emit `chp-click`, `chp-input`, `chp-change` — not native `click`/`input` semantics. In React wrappers these are `onChpClick`, `onChpInput`, `onChpChange`.
16
+
17
+ ## Quick reference
18
+
19
+ | Need | Use |
20
+ |---|---|
21
+ | Primary action button | `<chp-button variant="primary">` |
22
+ | Destructive action | `<chp-button variant="danger">` |
23
+ | Status pill (paid, pending, cancelled...) | `<chp-badge tone="success|warning|danger|info|neutral|brand">` |
24
+ | Text field with label + validation | `<chp-input label="..." error="...">` |
25
+ | Record/summary container | `<chp-card heading="...">` with optional `slot="actions"` |
26
+ | Page background | `var(--chp-bg-page)` |
27
+ | Card/surface background | `var(--chp-bg-surface)` |
28
+ | Body text color | `var(--chp-text-primary)` |
29
+ | Secondary/muted text | `var(--chp-text-secondary)` / `var(--chp-text-muted)` |
30
+ | Hairline divider | `border: 0.5px solid var(--chp-border)` |
31
+ | Brand orange | `var(--chp-primary)` (never raw `#E68524`) |
32
+
33
+ ## Design conventions
34
+
35
+ - Font is Inter (400 body, 500 medium, 600 headings/buttons). Type scale: 26/19/15px headings, 14px body, 13px secondary, 12px captions. Never go below 12px.
36
+ - Border radius: 8px for controls, 10px for cards.
37
+ - Text on the orange primary button is navy (`--chp-text-on-primary`), not white.
38
+ - Status badges are the standard way to show entity state in tables and cards.
39
+ - All text/background pairs in the tokens are WCAG AA checked — introducing new color combinations requires a contrast check (4.5:1 body, 3:1 large/UI).
40
+
41
+ ## Before adding a new component
42
+
43
+ Search `src/components/` first. If nothing fits, propose the new component in the design-system repo (github.com/chipmo/design-system) rather than building a one-off inside an ERP app — one-off components break cross-product consistency.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chipmo (chipmo.mn)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Chipmo design system
2
+
3
+ Shared tokens + components for every Chipmo ERP. One repo, one npm package, every product looks like Chipmo.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @chipmo/design-system
9
+ ```
10
+
11
+ Public on npm — no registry setup needed.
12
+
13
+ ## Use
14
+
15
+ ```js
16
+ // once, at app root
17
+ import '@chipmo/design-system/tokens.css';
18
+ import '@chipmo/design-system'; // registers <chp-*> elements
19
+ ```
20
+
21
+ ```html
22
+ <chp-card heading="Нэхэмжлэх #INV-2041">
23
+ <chp-badge slot="actions" tone="success">Төлөгдсөн</chp-badge>
24
+ <chp-input label="Харилцагч" value="Монгол Транс ХХК"></chp-input>
25
+ <chp-button variant="primary">Хадгалах</chp-button>
26
+ </chp-card>
27
+ ```
28
+
29
+ React: `import { Button, Badge, Input, Card } from '@chipmo/design-system/react'`
30
+ Vue 3: `import '@chipmo/design-system/vue'` — see `src/vue/index.js` for Vite config.
31
+
32
+ ## Try it in 30 seconds
33
+
34
+ A full working demo lives at [`examples/index.html`](examples/index.html) — buttons, badges, inputs, a card, and a light/dark toggle. It loads everything from CDN, so you can test the design system with **zero install**: save this as `demo.html` and open it in a browser.
35
+
36
+ ```html
37
+ <!doctype html>
38
+ <html lang="mn">
39
+ <head>
40
+ <link rel="stylesheet" href="https://unpkg.com/@chipmo/design-system/dist/tokens.css" />
41
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
42
+ <script type="module">
43
+ import 'https://esm.sh/@chipmo/design-system';
44
+ </script>
45
+ <style>
46
+ body { font-family: 'Inter', sans-serif; background: var(--chp-bg-page);
47
+ color: var(--chp-text-primary); padding: 2rem; }
48
+ </style>
49
+ </head>
50
+ <body>
51
+ <chp-card heading="Нэхэмжлэх #INV-2041" style="max-width: 420px">
52
+ <chp-badge slot="actions" tone="success">Төлөгдсөн</chp-badge>
53
+ <chp-input label="Харилцагч" value="Монгол Транс ХХК"></chp-input>
54
+ <br />
55
+ <chp-button variant="primary">Хадгалах</chp-button>
56
+ <chp-button variant="outline">Цуцлах</chp-button>
57
+ </chp-card>
58
+
59
+ <script>
60
+ // components emit chp-* events
61
+ document.querySelector('chp-button')
62
+ .addEventListener('chp-click', () => alert('Хадгалагдлаа'));
63
+ </script>
64
+ </body>
65
+ </html>
66
+ ```
67
+
68
+ Force dark mode by adding `data-theme="dark"` to the `<html>` tag — or leave it off and the tokens follow the visitor's system preference automatically.
69
+
70
+ The demo also deploys automatically to GitHub Pages on every merge to `main`:
71
+ **https://YOUR-ORG.github.io/design-system/** — the always-current showcase of the latest components and tokens.
72
+
73
+ ## Dark mode
74
+
75
+ Automatic via `prefers-color-scheme`. Force it with `<html data-theme="dark">`.
76
+
77
+ ## Working with AI coding tools
78
+
79
+ This repo is built to be used with AI pair programming. `CLAUDE.md` contains the rules
80
+ agents must follow; `custom-elements.json` (generated on build) is the machine-readable
81
+ component API; `llms.txt` is the index. Copy `CLAUDE.md` into consumer ERP repos or
82
+ reference it from theirs.
83
+
84
+ ## Developing
85
+
86
+ ```bash
87
+ npm install
88
+ npm run build # generates dist/tokens.css + custom-elements.json
89
+ ```
90
+
91
+ Edit colors only in `tokens/design-tokens.json`, then rebuild. Never edit `dist/`.
92
+
93
+ ## Releasing
94
+
95
+ Merging to `main` triggers CI to build and publish to npm. Consumer ERP
96
+ repos using Renovate pick up the new version automatically via auto-merged PRs.
@@ -0,0 +1,5 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "readme": "",
4
+ "modules": []
5
+ }
@@ -0,0 +1,98 @@
1
+ /* Generated by scripts/build-tokens.mjs — DO NOT EDIT BY HAND. Edit tokens/design-tokens.json instead. */
2
+
3
+ :root {
4
+ --chp-navy-50: #F0F4F9;
5
+ --chp-navy-100: #D9E3F2;
6
+ --chp-navy-200: #ACC1E2;
7
+ --chp-navy-300: #7093CD;
8
+ --chp-navy-400: #3965AC;
9
+ --chp-navy-500: #28487B;
10
+ --chp-navy-600: #1E365C;
11
+ --chp-navy-700: #172B4A;
12
+ --chp-navy-800: #13223B;
13
+ --chp-navy-900: #0A1424;
14
+ --chp-orange-50: #FEF5EB;
15
+ --chp-orange-100: #FDE7D3;
16
+ --chp-orange-200: #F9CEA9;
17
+ --chp-orange-300: #F4B67B;
18
+ --chp-orange-400: #EE9B4F;
19
+ --chp-orange-500: #E68524;
20
+ --chp-orange-600: #C9621D;
21
+ --chp-orange-700: #A74A1B;
22
+ --chp-orange-800: #823717;
23
+ --chp-orange-900: #5E2612;
24
+ --chp-success-400: #59C084;
25
+ --chp-success-500: #27864F;
26
+ --chp-warning-400: #F0BF4C;
27
+ --chp-warning-500: #E6A50F;
28
+ --chp-warning-700text: #916808;
29
+ --chp-danger-400: #E2635A;
30
+ --chp-danger-500: #C6342A;
31
+ --chp-info-400: #5FA2DD;
32
+ --chp-info-500: #2278C3;
33
+ }
34
+
35
+ :root, [data-theme="light"] {
36
+ --chp-bg-page: #FFFFFF;
37
+ --chp-bg-surface: #FFFFFF;
38
+ --chp-bg-subtle: #F0F4F9;
39
+ --chp-text-primary: #13223B;
40
+ --chp-text-secondary: #28487B;
41
+ --chp-text-muted: #7093CD;
42
+ --chp-border: #D9E3F2;
43
+ --chp-border-strong: #ACC1E2;
44
+ --chp-primary: #E68524;
45
+ --chp-primary-hover: #C9621D;
46
+ --chp-text-on-primary: #13223B;
47
+ --chp-accent-text: #A74A1B;
48
+ --chp-secondary: #13223B;
49
+ --chp-text-on-secondary: #FFFFFF;
50
+ --chp-success: #27864F;
51
+ --chp-warning: #916808;
52
+ --chp-danger: #C6342A;
53
+ --chp-info: #2278C3;
54
+ }
55
+
56
+ [data-theme="dark"] {
57
+ --chp-bg-page: #0A1424;
58
+ --chp-bg-surface: #13223B;
59
+ --chp-bg-subtle: #1E365C;
60
+ --chp-text-primary: #F0F4F9;
61
+ --chp-text-secondary: #ACC1E2;
62
+ --chp-text-muted: #7093CD;
63
+ --chp-border: #1E365C;
64
+ --chp-border-strong: #28487B;
65
+ --chp-primary: #EE9B4F;
66
+ --chp-primary-hover: #F4B67B;
67
+ --chp-text-on-primary: #13223B;
68
+ --chp-accent-text: #EE9B4F;
69
+ --chp-secondary: #ACC1E2;
70
+ --chp-text-on-secondary: #0A1424;
71
+ --chp-success: #59C084;
72
+ --chp-warning: #F0BF4C;
73
+ --chp-danger: #E2635A;
74
+ --chp-info: #5FA2DD;
75
+ }
76
+
77
+ @media (prefers-color-scheme: dark) {
78
+ :root:not([data-theme="light"]) {
79
+ --chp-bg-page: #0A1424;
80
+ --chp-bg-surface: #13223B;
81
+ --chp-bg-subtle: #1E365C;
82
+ --chp-text-primary: #F0F4F9;
83
+ --chp-text-secondary: #ACC1E2;
84
+ --chp-text-muted: #7093CD;
85
+ --chp-border: #1E365C;
86
+ --chp-border-strong: #28487B;
87
+ --chp-primary: #EE9B4F;
88
+ --chp-primary-hover: #F4B67B;
89
+ --chp-text-on-primary: #13223B;
90
+ --chp-accent-text: #EE9B4F;
91
+ --chp-secondary: #ACC1E2;
92
+ --chp-text-on-secondary: #0A1424;
93
+ --chp-success: #59C084;
94
+ --chp-warning: #F0BF4C;
95
+ --chp-danger: #E2635A;
96
+ --chp-info: #5FA2DD;
97
+ }
98
+ }
package/llms.txt ADDED
@@ -0,0 +1,23 @@
1
+ # @chipmo/design-system
2
+
3
+ > Shared design tokens and web components for all Chipmo (chipmo.mn) ERP products.
4
+ > Framework-agnostic core (Lit web components) with React and Vue adapters.
5
+ > Brand colors: navy #13223B and orange #E68524, extracted from the Chipmo logo.
6
+
7
+ ## Key files
8
+ - CLAUDE.md: rules for AI coding agents working with this system (read this first)
9
+ - tokens/design-tokens.json: single source of truth for all colors, light + dark mode
10
+ - custom-elements.json: machine-readable manifest of every component API
11
+ - src/components/: Lit component sources with JSDoc
12
+
13
+ ## Components
14
+ - chp-button: variants primary|secondary|outline|ghost|danger, sizes sm|md|lg, event chp-click
15
+ - chp-badge: tones neutral|success|warning|danger|info|brand
16
+ - chp-input: label, error, placeholder, events chp-input / chp-change
17
+ - chp-card: heading attr, default slot + actions slot
18
+
19
+ ## Usage
20
+ - import '@chipmo/design-system' registers all custom elements
21
+ - import '@chipmo/design-system/tokens.css' provides all --chp-* CSS variables
22
+ - React: import { Button } from '@chipmo/design-system/react'
23
+ - Dark mode: automatic via prefers-color-scheme, or data-theme="dark" on html
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@chipmo/design-system",
3
+ "version": "1.0.0",
4
+ "description": "Chipmo design system: tokens + web components shared across all Chipmo ERP products. Works in React, Vue, and plain HTML.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/chipmo/design-system.git"
10
+ },
11
+ "files": [
12
+ "src",
13
+ "dist",
14
+ "tokens",
15
+ "custom-elements.json",
16
+ "CLAUDE.md",
17
+ "llms.txt"
18
+ ],
19
+ "exports": {
20
+ ".": "./src/index.js",
21
+ "./react": "./src/react/index.js",
22
+ "./vue": "./src/vue/index.js",
23
+ "./tokens.css": "./dist/tokens.css",
24
+ "./tokens.json": "./tokens/design-tokens.json"
25
+ },
26
+ "customElements": "custom-elements.json",
27
+ "scripts": {
28
+ "build": "node scripts/build-tokens.mjs && npm run manifest",
29
+ "manifest": "custom-elements-manifest analyze --litelement --globs 'src/components/*.js'",
30
+ "test": "node --test"
31
+ },
32
+ "dependencies": {
33
+ "lit": "^3.1.0"
34
+ },
35
+ "peerDependencies": {
36
+ "@lit/react": "^1.0.0",
37
+ "react": ">=17"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@lit/react": {
41
+ "optional": true
42
+ },
43
+ "react": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "devDependencies": {
48
+ "@custom-elements-manifest/analyzer": "^0.10.0"
49
+ },
50
+ "keywords": [
51
+ "design-system",
52
+ "web-components",
53
+ "lit",
54
+ "chipmo",
55
+ "erp",
56
+ "design-tokens"
57
+ ],
58
+ "homepage": "https://github.com/chipmo/design-system#readme"
59
+ }
@@ -0,0 +1,42 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ /**
4
+ * Chipmo status badge (pill). The ERP workhorse for invoice/order/stock states.
5
+ *
6
+ * @tag chp-badge
7
+ * @attr {("neutral"|"success"|"warning"|"danger"|"info"|"brand")} tone - Semantic color. Default "neutral".
8
+ * @slot - Badge label.
9
+ *
10
+ * @example <chp-badge tone="success">Төлөгдсөн</chp-badge>
11
+ * @example <chp-badge tone="warning">Хүлээгдэж буй</chp-badge>
12
+ */
13
+ export class ChpBadge extends LitElement {
14
+ static properties = { tone: { type: String, reflect: true } };
15
+
16
+ constructor() {
17
+ super();
18
+ this.tone = 'neutral';
19
+ }
20
+
21
+ static styles = css`
22
+ :host { display: inline-block; }
23
+ span {
24
+ font-family: var(--chp-font-sans, 'Inter', sans-serif);
25
+ font-size: 12px; font-weight: 500;
26
+ padding: 4px 10px; border-radius: 99px;
27
+ display: inline-block; line-height: 1.3;
28
+ }
29
+ :host([tone='neutral']) span { background: var(--chp-bg-subtle); color: var(--chp-text-secondary); }
30
+ :host([tone='brand']) span { background: var(--chp-orange-100); color: var(--chp-orange-800); }
31
+ :host([tone='success']) span { background: color-mix(in srgb, var(--chp-success) 14%, var(--chp-bg-surface)); color: var(--chp-success); }
32
+ :host([tone='warning']) span { background: color-mix(in srgb, var(--chp-warning) 14%, var(--chp-bg-surface)); color: var(--chp-warning); }
33
+ :host([tone='danger']) span { background: color-mix(in srgb, var(--chp-danger) 14%, var(--chp-bg-surface)); color: var(--chp-danger); }
34
+ :host([tone='info']) span { background: color-mix(in srgb, var(--chp-info) 14%, var(--chp-bg-surface)); color: var(--chp-info); }
35
+ `;
36
+
37
+ render() {
38
+ return html`<span part="badge"><slot></slot></span>`;
39
+ }
40
+ }
41
+
42
+ customElements.define('chp-badge', ChpBadge);
@@ -0,0 +1,78 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ /**
4
+ * Chipmo button.
5
+ *
6
+ * @tag chp-button
7
+ * @attr {("primary"|"secondary"|"outline"|"ghost"|"danger")} variant - Visual style. Default "primary".
8
+ * @attr {boolean} disabled - Disables the button.
9
+ * @attr {("sm"|"md"|"lg")} size - Control height. Default "md".
10
+ * @fires chp-click - Fired on activation (click or Enter/Space). Not fired when disabled.
11
+ * @slot - Button label.
12
+ *
13
+ * @example <chp-button variant="primary">Хадгалах</chp-button>
14
+ * @example <chp-button variant="danger" size="sm">Устгах</chp-button>
15
+ */
16
+ export class ChpButton extends LitElement {
17
+ static properties = {
18
+ variant: { type: String, reflect: true },
19
+ size: { type: String, reflect: true },
20
+ disabled: { type: Boolean, reflect: true },
21
+ };
22
+
23
+ constructor() {
24
+ super();
25
+ this.variant = 'primary';
26
+ this.size = 'md';
27
+ this.disabled = false;
28
+ }
29
+
30
+ static styles = css`
31
+ :host { display: inline-block; }
32
+ button {
33
+ font-family: var(--chp-font-sans, 'Inter', sans-serif);
34
+ font-weight: 600;
35
+ border: none;
36
+ border-radius: 8px;
37
+ cursor: pointer;
38
+ transition: background-color 0.15s ease;
39
+ line-height: 1;
40
+ }
41
+ :host([size='sm']) button { padding: 6px 12px; font-size: 13px; }
42
+ button, :host([size='md']) button { padding: 9px 18px; font-size: 14px; }
43
+ :host([size='lg']) button { padding: 12px 24px; font-size: 15px; }
44
+
45
+ :host([variant='primary']) button { background: var(--chp-primary); color: var(--chp-text-on-primary); }
46
+ :host([variant='primary']) button:hover:not(:disabled) { background: var(--chp-primary-hover); }
47
+
48
+ :host([variant='secondary']) button { background: var(--chp-secondary); color: var(--chp-text-on-secondary); font-weight: 500; }
49
+
50
+ :host([variant='outline']) button {
51
+ background: transparent; color: var(--chp-text-primary);
52
+ border: 1px solid var(--chp-border-strong); font-weight: 500;
53
+ }
54
+ :host([variant='outline']) button:hover:not(:disabled) { background: var(--chp-bg-subtle); }
55
+
56
+ :host([variant='ghost']) button { background: transparent; color: var(--chp-accent-text); font-weight: 500; }
57
+
58
+ :host([variant='danger']) button { background: var(--chp-danger); color: #fff; font-weight: 500; }
59
+
60
+ button:disabled { background: var(--chp-bg-subtle); color: var(--chp-text-muted); cursor: not-allowed; }
61
+ button:focus-visible { outline: 2px solid var(--chp-primary); outline-offset: 2px; }
62
+ `;
63
+
64
+ render() {
65
+ return html`<button
66
+ ?disabled=${this.disabled}
67
+ @click=${this._onClick}
68
+ part="button"
69
+ ><slot></slot></button>`;
70
+ }
71
+
72
+ _onClick(e) {
73
+ if (this.disabled) return;
74
+ this.dispatchEvent(new CustomEvent('chp-click', { bubbles: true, composed: true, detail: { originalEvent: e } }));
75
+ }
76
+ }
77
+
78
+ customElements.define('chp-button', ChpButton);
@@ -0,0 +1,60 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ /**
4
+ * Chipmo card container. Standard surface for records, summaries, and panels.
5
+ *
6
+ * @tag chp-card
7
+ * @attr {string} heading - Optional card title rendered in the header row.
8
+ * @attr {boolean} subtle - Uses the subtle background instead of the raised surface.
9
+ * @slot - Card body content.
10
+ * @slot actions - Right side of the header row (badges, buttons).
11
+ *
12
+ * @example
13
+ * <chp-card heading="Нэхэмжлэх #INV-2041">
14
+ * <chp-badge slot="actions" tone="success">Төлөгдсөн</chp-badge>
15
+ * ...rows...
16
+ * </chp-card>
17
+ */
18
+ export class ChpCard extends LitElement {
19
+ static properties = {
20
+ heading: { type: String },
21
+ subtle: { type: Boolean, reflect: true },
22
+ };
23
+
24
+ constructor() {
25
+ super();
26
+ this.heading = '';
27
+ this.subtle = false;
28
+ }
29
+
30
+ static styles = css`
31
+ :host {
32
+ display: block;
33
+ font-family: var(--chp-font-sans, 'Inter', sans-serif);
34
+ background: var(--chp-bg-surface);
35
+ border: 0.5px solid var(--chp-border);
36
+ border-radius: 10px;
37
+ padding: 1rem 1.25rem;
38
+ }
39
+ :host([subtle]) { background: var(--chp-bg-subtle); }
40
+ header {
41
+ display: flex; justify-content: space-between; align-items: center;
42
+ margin-bottom: 10px;
43
+ }
44
+ h3 { font-size: 14px; font-weight: 600; color: var(--chp-text-primary); margin: 0; }
45
+ `;
46
+
47
+ render() {
48
+ return html`
49
+ ${this.heading
50
+ ? html`<header part="header">
51
+ <h3>${this.heading}</h3>
52
+ <slot name="actions"></slot>
53
+ </header>`
54
+ : ''}
55
+ <slot></slot>
56
+ `;
57
+ }
58
+ }
59
+
60
+ customElements.define('chp-card', ChpCard);
@@ -0,0 +1,93 @@
1
+ import { LitElement, html, css } from 'lit';
2
+
3
+ /**
4
+ * Chipmo text input with optional label and error message.
5
+ *
6
+ * @tag chp-input
7
+ * @attr {string} label - Field label rendered above the input.
8
+ * @attr {string} value - Current value.
9
+ * @attr {string} placeholder - Placeholder text.
10
+ * @attr {string} error - Error message. When set, the input shows the danger border and the message below.
11
+ * @attr {string} type - Input type (text, email, number, password...). Default "text".
12
+ * @attr {boolean} disabled - Disables the input.
13
+ * @fires chp-input - Fired on every input, detail: { value }.
14
+ * @fires chp-change - Fired on change (blur after edit), detail: { value }.
15
+ *
16
+ * @example <chp-input label="Нэр" placeholder="Харилцагчийн нэр"></chp-input>
17
+ * @example <chp-input label="Имэйл" type="email" error="Имэйл буруу байна"></chp-input>
18
+ */
19
+ export class ChpInput extends LitElement {
20
+ static properties = {
21
+ label: { type: String },
22
+ value: { type: String },
23
+ placeholder: { type: String },
24
+ error: { type: String },
25
+ type: { type: String },
26
+ disabled: { type: Boolean, reflect: true },
27
+ };
28
+
29
+ constructor() {
30
+ super();
31
+ this.label = '';
32
+ this.value = '';
33
+ this.placeholder = '';
34
+ this.error = '';
35
+ this.type = 'text';
36
+ this.disabled = false;
37
+ }
38
+
39
+ static styles = css`
40
+ :host { display: block; font-family: var(--chp-font-sans, 'Inter', sans-serif); }
41
+ label { display: block; font-size: 13px; font-weight: 500; color: var(--chp-text-primary); margin-bottom: 6px; }
42
+ input {
43
+ width: 100%; box-sizing: border-box;
44
+ font-family: inherit; font-size: 14px;
45
+ color: var(--chp-text-primary);
46
+ background: var(--chp-bg-surface);
47
+ border: 1px solid var(--chp-border-strong);
48
+ border-radius: 8px; padding: 9px 12px;
49
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
50
+ }
51
+ input::placeholder { color: var(--chp-text-muted); }
52
+ input:focus {
53
+ outline: none;
54
+ border-color: var(--chp-primary);
55
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--chp-primary) 22%, transparent);
56
+ }
57
+ input:disabled { background: var(--chp-bg-subtle); color: var(--chp-text-muted); cursor: not-allowed; }
58
+ :host([data-invalid]) input { border-color: var(--chp-danger); }
59
+ .error { font-size: 12px; color: var(--chp-danger); margin-top: 5px; }
60
+ `;
61
+
62
+ updated() {
63
+ this.toggleAttribute('data-invalid', Boolean(this.error));
64
+ }
65
+
66
+ render() {
67
+ return html`
68
+ ${this.label ? html`<label for="field">${this.label}</label>` : ''}
69
+ <input
70
+ id="field"
71
+ part="input"
72
+ .type=${this.type}
73
+ .value=${this.value}
74
+ placeholder=${this.placeholder}
75
+ ?disabled=${this.disabled}
76
+ @input=${this._onInput}
77
+ @change=${this._onChange}
78
+ />
79
+ ${this.error ? html`<div class="error" part="error">${this.error}</div>` : ''}
80
+ `;
81
+ }
82
+
83
+ _onInput(e) {
84
+ this.value = e.target.value;
85
+ this.dispatchEvent(new CustomEvent('chp-input', { bubbles: true, composed: true, detail: { value: this.value } }));
86
+ }
87
+
88
+ _onChange(e) {
89
+ this.dispatchEvent(new CustomEvent('chp-change', { bubbles: true, composed: true, detail: { value: e.target.value } }));
90
+ }
91
+ }
92
+
93
+ customElements.define('chp-input', ChpInput);
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { ChpButton } from './components/chp-button.js';
2
+ export { ChpBadge } from './components/chp-badge.js';
3
+ export { ChpInput } from './components/chp-input.js';
4
+ export { ChpCard } from './components/chp-card.js';
@@ -0,0 +1,32 @@
1
+ import { createComponent } from '@lit/react';
2
+ import React from 'react';
3
+ import { ChpButton } from '../components/chp-button.js';
4
+ import { ChpBadge } from '../components/chp-badge.js';
5
+ import { ChpInput } from '../components/chp-input.js';
6
+ import { ChpCard } from '../components/chp-card.js';
7
+
8
+ export const Button = createComponent({
9
+ tagName: 'chp-button',
10
+ elementClass: ChpButton,
11
+ react: React,
12
+ events: { onChpClick: 'chp-click' },
13
+ });
14
+
15
+ export const Badge = createComponent({
16
+ tagName: 'chp-badge',
17
+ elementClass: ChpBadge,
18
+ react: React,
19
+ });
20
+
21
+ export const Input = createComponent({
22
+ tagName: 'chp-input',
23
+ elementClass: ChpInput,
24
+ react: React,
25
+ events: { onChpInput: 'chp-input', onChpChange: 'chp-change' },
26
+ });
27
+
28
+ export const Card = createComponent({
29
+ tagName: 'chp-card',
30
+ elementClass: ChpCard,
31
+ react: React,
32
+ });
@@ -0,0 +1,11 @@
1
+ // Vue 3 consumes web components natively. Import this module once in main.js
2
+ // to register all Chipmo elements, then use <chp-button>, <chp-input>, etc.
3
+ // directly in templates. Configure Vite to treat chp-* as custom elements:
4
+ //
5
+ // // vite.config.js
6
+ // vue({ template: { compilerOptions: {
7
+ // isCustomElement: (tag) => tag.startsWith('chp-')
8
+ // }}})
9
+ //
10
+ // Listen to events with @chp-click, @chp-input, @chp-change.
11
+ import '../index.js';
@@ -0,0 +1,98 @@
1
+ {
2
+ "$schema": "https://chipmo.mn/design-system/tokens.schema.json",
3
+ "$description": "Chipmo design system color tokens. Source of truth for all ERP products. Brand colors were extracted directly from the chipmo.mn logo. Do not hardcode hex values in application code — always reference these tokens by name.",
4
+ "$version": "1.0.0",
5
+ "$lastUpdated": "2026-07-06",
6
+
7
+ "brand": {
8
+ "navy": { "value": "#13223B", "description": "Primary brand color, extracted from logo wordmark and ring." },
9
+ "orange": { "value": "#E68524", "description": "Secondary brand color, extracted from logo mark center." }
10
+ },
11
+
12
+ "ramp": {
13
+ "navy": {
14
+ "50": "#F0F4F9",
15
+ "100": "#D9E3F2",
16
+ "200": "#ACC1E2",
17
+ "300": "#7093CD",
18
+ "400": "#3965AC",
19
+ "500": "#28487B",
20
+ "600": "#1E365C",
21
+ "700": "#172B4A",
22
+ "800": "#13223B",
23
+ "900": "#0A1424"
24
+ },
25
+ "orange": {
26
+ "50": "#FEF5EB",
27
+ "100": "#FDE7D3",
28
+ "200": "#F9CEA9",
29
+ "300": "#F4B67B",
30
+ "400": "#EE9B4F",
31
+ "500": "#E68524",
32
+ "600": "#C9621D",
33
+ "700": "#A74A1B",
34
+ "800": "#823717",
35
+ "900": "#5E2612"
36
+ },
37
+ "success": { "500": "#27864F", "400": "#59C084" },
38
+ "warning": { "700text": "#916808", "500": "#E6A50F", "400": "#F0BF4C" },
39
+ "danger": { "500": "#C6342A", "400": "#E2635A" },
40
+ "info": { "500": "#2278C3", "400": "#5FA2DD" }
41
+ },
42
+
43
+ "light": {
44
+ "$description": "Semantic tokens for light mode. Reference these, never the raw ramp values, in components.",
45
+ "bg-page": "#FFFFFF",
46
+ "bg-surface": "#FFFFFF",
47
+ "bg-subtle": "{ramp.navy.50}",
48
+ "text-primary": "{ramp.navy.800}",
49
+ "text-secondary": "{ramp.navy.500}",
50
+ "text-muted": "{ramp.navy.300}",
51
+ "border": "{ramp.navy.100}",
52
+ "border-strong": "{ramp.navy.200}",
53
+ "primary": "{ramp.orange.500}",
54
+ "primary-hover": "{ramp.orange.600}",
55
+ "text-on-primary": "{ramp.navy.800}",
56
+ "accent-text": "{ramp.orange.700}",
57
+ "secondary": "{ramp.navy.800}",
58
+ "text-on-secondary": "#FFFFFF",
59
+ "success": "{ramp.success.500}",
60
+ "warning": "{ramp.warning.700text}",
61
+ "danger": "{ramp.danger.500}",
62
+ "info": "{ramp.info.500}"
63
+ },
64
+
65
+ "dark": {
66
+ "$description": "Semantic tokens for dark mode. Reference these, never the raw ramp values, in components.",
67
+ "bg-page": "{ramp.navy.900}",
68
+ "bg-surface": "{ramp.navy.800}",
69
+ "bg-subtle": "{ramp.navy.600}",
70
+ "text-primary": "{ramp.navy.50}",
71
+ "text-secondary": "{ramp.navy.200}",
72
+ "text-muted": "{ramp.navy.300}",
73
+ "border": "{ramp.navy.600}",
74
+ "border-strong": "{ramp.navy.500}",
75
+ "primary": "{ramp.orange.400}",
76
+ "primary-hover": "{ramp.orange.300}",
77
+ "text-on-primary": "{ramp.navy.800}",
78
+ "accent-text": "{ramp.orange.400}",
79
+ "secondary": "{ramp.navy.200}",
80
+ "text-on-secondary": "{ramp.navy.900}",
81
+ "success": "{ramp.success.400}",
82
+ "warning": "{ramp.warning.400}",
83
+ "danger": "{ramp.danger.400}",
84
+ "info": "{ramp.info.400}"
85
+ },
86
+
87
+ "accessibility": {
88
+ "$description": "Contrast ratios verified against WCAG 2.1 AA (4.5:1 body text, 3:1 large text/UI components).",
89
+ "checked": [
90
+ { "pair": "light.text-primary on light.bg-page", "ratio": 15.94, "pass": "AA" },
91
+ { "pair": "light.text-secondary on light.bg-page", "ratio": 9.12, "pass": "AA" },
92
+ { "pair": "light.accent-text on light.bg-page", "ratio": 5.77, "pass": "AA" },
93
+ { "pair": "light.text-on-primary on light.primary", "ratio": 5.9, "pass": "AA" },
94
+ { "pair": "dark.text-primary on dark.bg-page", "ratio": 18.45, "pass": "AA" },
95
+ { "pair": "dark.text-on-primary on dark.primary", "ratio": 9.44, "pass": "AA" }
96
+ ]
97
+ }
98
+ }