Applies myKaarma's official brand colors, typography, spacing, navigation patterns, dark mode, and interaction design to all UI output. Invoke this skill before starting any UI project — components, dashboards, forms, or AI-generated artifacts — to ensure full consistency with myKaarma's visual identity and design system.
Use this skill when:
Keywords: branding, colors, typography, tokens, styling, UI, dashboard, form, navigation, sidebar, dark mode, theme, components, myKaarma
Always reference design tokens by their CSS custom property name. Every token must include an inline hex fallback so the UI renders correctly even if theme.css cannot be fetched.
/* ❌ Never — no token, no fallback */
.header { color: #0377B3; font-size: 16px; }
/* ❌ Never — token without fallback */
.header { color: var(--primary-color); }
/* ✅ Always — token + inline hex fallback */
.header { color: var(--primary-color, #0377B3); }
// ❌ No fallback
<div style={{ color: 'var(--primary-color)' }}>
// ✅ With fallback
<div style={{ color: 'var(--primary-color, #0377B3)' }}>
Why fallbacks matter: AI tools may not be able to fetch theme.css due to network restrictions or context limits. Inline fallbacks ensure the correct brand color renders regardless.
| Resource | URL |
|---|---|
| Design tokens (root theme) | https://static.mykaarma.com/res/mkblue/css/theme.css |
| Typography classes | https://static.mykaarma.com/res/global/css/text-size.css |
| Button styles | https://static.mykaarma.com/res/global/css/mk-button.css |
When these files cannot be fetched, use the hex fallback values defined in this document.
| Token | CSS Variable | Hex Fallback | When to use |
|---|---|---|---|
| Primary | --primary-color | #0377B3 | CTAs, active states, links, focus rings, chart primaries |
| Secondary | --secondary-color | #D3E4F1 | Tag backgrounds, selected row highlights, nav active bg |
| Tertiary | --tertiary-color | #535862 | Secondary text, metadata, labels |
| Quaternary | --quaternary-color | #FFFFFF | Card surfaces, white-on-dark text |
| Disabled | --disabled-color | #E9EAEB | Disabled inputs, inactive surfaces |
| Primary Accent | --primary-accent | #ED6D22 | Logo mark only + warnings, attention states |
| Secondary Accent | --secondary-accent | #BADA55 | Positive indicators, success badges |
| Tertiary Accent | --tertiary-accent | #FFE0B6 | Subtle warm highlights, low-priority badges |
| Token | CSS Variable | Hex Fallback | When to use |
|---|---|---|---|
| Main | --bg-main | #FFFFFF | Page background |
| Base 1 | --bg-base-1 | #FBFBFB | Card and section backgrounds |
| Base 2 | --bg-base-2 | #D8D8D8 | Borders, dividers |
| Base 3 | --bg-base-3 | #B8B8B8 | Muted elements, placeholder fills |
| Status | Foreground Token | Hex | Background Token | Hex |
|---|---|---|---|---|
| Error | --Red-700 | #B42318 | --Red-50 | #FEF3F2 |
| Success | --Green-700 | #027A48 | --Green-50 | #ECFDF3 |
| Warning | --primary-accent | #ED6D22 | --tertiary-accent | #FFE0B6 |
| Info | --primary-color | #0377B3 | --secondary-color | #D3E4F1 |
--primary-color only for: primary buttons, active nav items, links, focus rings, primary chart series.--primary-accent (orange) only for: the logo mark background, warnings, attention-required states. Never for primary actions.--secondary-accent (lime) only for: success/positive status indicators.--tertiary-color or --disabled-color. Never style inactive text in blue.| Role | Token | Hex Fallback |
|---|---|---|
| Page background | --bg-main | #0F1117 |
| Card / panel | --bg-base-1 | #1A1F2E |
| Elevated surface | --bg-base-2 | #232839 |
| Border / divider | --bg-base-3 | #2E3347 |
| Input background | --bg-dark-1 | #1E2433 |
Derivation logic:
#0F1117is the brand navy pushed to near-black with its cool blue undertone preserved. Each surface step adds ~8% lightness. This keeps dark mode feeling like myKaarma without using pure black.
| Role | Hex Fallback |
|---|---|
| Primary text | #F0F4F8 |
| Secondary text | #8B95A6 |
| Disabled / muted | #4A5568 |
| Role | Hex Fallback | Notes |
|---|---|---|
| Primary action / brand | #3B9DD4 | Lightened from #0377B3 for dark bg contrast |
| Secondary button border + text | #7EB8D9 | Clearly visible on #1A1F2E topbar background |
| Focus ring | rgba(59,157,212,0.4) | 40% opacity of dark primary |
| Hover surface | rgba(255,255,255,0.05) | 5% white overlay |
| Status | Text Hex | Background Hex |
|---|---|---|
| Error | #F97066 | #1C0A09 |
| Success | #32D583 | #052E16 |
| Warning | #FD853A | #1C0F04 |
| Info | #36ADEF | #031527 |
Scope all token overrides to #shell.light and #shell.dark. Never use .light or .dark alone — the host environment may have its own classes that conflict.
#shell.light {
--bg-page: #F5F7FA;
--bg-sidebar: #FFFFFF;
--bg-topbar: #FFFFFF;
--bg-card: #FFFFFF;
--border: #E9EAEB;
--text-primary: #1A1A2E;
--text-secondary: #535862;
--text-muted: #B8B8B8;
--brand: #0377B3;
--nav-active-bg: #D3E4F1;
--nav-active-text: #0377B3;
--nav-dot-default: #D8D8D8;
--btn-sec-color: #0377B3;
--toggle-track: #E9EAEB;
--avatar-bg: #D3E4F1;
--avatar-text: #0377B3;
--chip-bg: #FFFFFF;
--chip-border: #D8D8D8;
--chip-text: #535862;
--chip-active-bg: #D3E4F1;
--chip-active-border: #0377B3;
--chip-active-text: #0377B3;
--input-bg: #FFFFFF;
--row-hover: #F0F6FB;
}
#shell.dark {
--bg-page: #0F1117;
--bg-sidebar: #1A1F2E;
--bg-topbar: #1A1F2E;
--bg-card: #232839;
--border: #2E3347;
--text-primary: #F0F4F8;
--text-secondary: #8B95A6;
--text-muted: #4A5568;
--brand: #3B9DD4;
--nav-active-bg: rgba(59,157,212,0.18);
--nav-active-text: #3B9DD4;
--nav-dot-default: #2E3347;
--btn-sec-color: #7EB8D9;
--toggle-track: #3B9DD4;
--avatar-bg: rgba(59,157,212,0.2);
--avatar-text: #3B9DD4;
--chip-bg: transparent;
--chip-border: #3B4560;
--chip-text: #8B95A6;
--chip-active-bg: rgba(59,157,212,0.18);
--chip-active-border: #3B9DD4;
--chip-active-text: #3B9DD4;
--input-bg: #1E2433;
--row-hover: rgba(255,255,255,0.04);
}
#000000) as background. Use #0F1117.#FFFFFF) as body text on dark surfaces. Use #F0F4F8.border: 1px solid var(--border, #2E3347) instead.initTheme() synchronously in <head> before first render to prevent flash of wrong theme.#shell.light / #shell.dark — never bare .light / .dark.// Run in <head> before first render
function initTheme() {
const stored = localStorage.getItem('mk-theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
document.getElementById('shell').className = stored || (prefersDark ? 'dark' : 'light');
}
initTheme();
function toggleTheme() {
const shell = document.getElementById('shell');
const next = shell.className === 'dark' ? 'light' : 'dark';
shell.className = next;
localStorage.setItem('mk-theme', next);
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (!localStorage.getItem('mk-theme')) {
document.getElementById('shell').className = e.matches ? 'dark' : 'light';
}
});
Full 25–900 scales are available for Brand, Gray, Red, Orange, Yellow, Green, Warm-Green, Light-blue, Purple, and Yellow-Brown. Reference as --{Palette}-{shade} (e.g., --Brand-500, --Gray-200).
Critical rule: never reference extended scale tokens directly in component code. Use them only inside token definition blocks (e.g., inside #shell.light { } or #shell.dark { }). In component code, always use the semantic tokens defined above (--primary-color, --brand, etc.) with their inline fallbacks.
/* ❌ Never in component code */
.badge { color: var(--Green-700); }
/* ✅ In component code — use semantic token + fallback */
.badge { color: var(--badge-act-fg, #027A48); }
/* ✅ Scale tokens are fine inside token definition blocks */
#shell.light {
--badge-act-fg: var(--Green-700, #027A48);
}
Font: Lato only. Weights: 400 (body), 700 (headings, buttons). Fallback: sans-serif.
Use .myk- prefixed classes exclusively. Never use .mk- prefixed classes (legacy, backward compat only). Never define custom font sizes inline.
| Class | Size | Line Height | Weight | Usage |
|---|---|---|---|---|
.myk-h1 | 2rem (32px) | 2.5rem | 700 | Page titles |
.myk-h2 | 1.5rem (24px) | 2rem | 700 | Section headers |
.myk-h3 | 1.25rem (20px) | 1.75rem | 700 | Subsection headers |
.myk-h4 | 1.125rem (18px) | 1.5rem | 700 | Card headers |
.myk-h5 | 1rem (16px) | 1.5rem | 700 | Small headers |
.myk-h6 | 0.875rem (14px) | 1.25rem | 700 | Minimal headers |
.myk-body1 | 1rem (16px) | 1.5rem | 400 | Primary body text |
.myk-body2 | 0.875rem (14px) | 1.25rem | 400 | Secondary body, table cells |
.myk-subtitle1 | 0.75rem (12px) | 1rem | 400 | Captions, metadata, timestamps |
.myk-subtitle2 | 0.625rem (10px) | 1.1rem | 400 | Fine print, tooltips |
.myk-button-lg | 1rem | 1.5rem | 700 | Large buttons — uppercase, 0.08em spacing |
.myk-button-reg | 0.875rem | 1.25rem | 700 | Regular buttons — uppercase, 0.08em spacing |
.myk-button-sm | 0.75rem | 1rem | 700 | Small buttons — uppercase, 0.08em spacing |
700), var(--text-primary, #1A1A2E)400), var(--text-secondary, #535862)var(--text-muted, #B8B8B8) — never blue or any active colorbackground: var(--bg-card, #FFFFFF); border: 1px solid var(--border, #E9EAEB); border-radius: 8pxvar(--space-5, 24px)| Token | Value | Fallback | Use |
|---|---|---|---|
--space-1 | 4px | 4px | Icon gaps, badge padding |
--space-2 | 8px | 8px | Component internal padding |
--space-3 | 12px | 12px | Compact row padding |
--space-4 | 16px | 16px | Standard card padding |
--space-5 | 24px | 24px | Section spacing |
--space-6 | 32px | 32px | Page-level section gaps |
--space-8 | 48px | 48px | Large layout separators |
| Token | Value | Use |
|---|---|---|
--radius-sm | 4px | Badges, tags, chips |
--radius-md | 6px | Inputs, selects |
--radius-lg | 8px | Cards, buttons |
--radius-xl | 12px | Modals, panels |
| Mode | Row height | Font | Use when |
|---|---|---|---|
| Compact | 32px | .myk-subtitle1 | Dense tables, audit logs |
| Default | 40px | .myk-body2 | Standard lists |
| Comfortable | 48px | .myk-body1 | Forms, settings |
The system is intentionally flat. Default to borders and background contrast. Use shadows only for floating elements.
| Token | Value | Use |
|---|---|---|
--shadow-xs | 0 1px 2px rgba(10,13,18,0.05) | Default card lift |
--shadow-sm | 0 1px 3px rgba(10,13,18,0.10) | Focused inputs, hovered cards |
--shadow-md | 0 4px 8px rgba(10,13,18,0.12) | Dropdowns, popovers |
--shadow-lg | 0 8px 16px rgba(10,13,18,0.15) | Modals, sheets |
Dark mode: use border: 1px solid var(--border, #2E3347) instead of shadows for card separation.
┌──────────────────────────────────────────────────────┐
│ Sidebar (220px) │ Top Bar (full width) │
│ │ Page title + breadcrumb │ Actions │
│ Logo (52px tall) ├───────────────────────────────────-│
│ │ │
│ Nav groups │ Page content │
│ └ Nav items │ │
│ (max 10 total) │ │
│ ──────────────── │ │
│ [JD] [☀ toggle] │ │
└──────────────────────────────────────────────────────┘
#ED6D22 logo mark (always), var(--brand) wordmarkborder on #shell — sidebar uses only border-right: 1px solid var(--border)Nav item states:
| State | Background | Text | Weight |
|---|---|---|---|
| Default | transparent | var(--text-secondary, #535862) | 400 |
| Hover (light) | #F5F7FA | var(--text-secondary, #535862) | 400 |
| Hover (dark) | rgba(255,255,255,0.05) | var(--text-secondary, #8B95A6) | 400 |
| Active | var(--nav-active-bg) | var(--nav-active-text) | 700 |
| Disabled | transparent | var(--disabled-color, #E9EAEB) | 400 |
.myk-h5) + breadcrumb below (.myk-subtitle1, muted)// ✅ Correct top bar
<header style={{
background: 'var(--bg-topbar, #FFFFFF)',
borderBottom: '1px solid var(--border, #E9EAEB)',
height: 52, display: 'flex', alignItems: 'center', padding: '0 20px', gap: 12
}}>
<div>
<h5 className="myk-h5">Conversations</h5>
<nav style={{ fontSize: 11, color: 'var(--text-muted, #B8B8B8)' }}>Home › Conversations</nav>
</div>
<div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
<button className="tb-btn secondary">Export</button>
<button className="tb-btn primary">+ New</button>
</div>
</header>
<div style={{ marginTop: 'auto', padding: '10px 8px', borderTop: '1px solid var(--border, #E9EAEB)', display: 'flex', alignItems: 'center', gap: 8 }}>
<div className="avatar">JD</div>
<button
onClick={toggleTheme}
aria-label="Toggle dark mode"
role="switch"
aria-checked={dark}
style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 6, background: 'none', border: 'none', cursor: 'pointer' }}
>
<span style={{ fontSize: 10, color: 'var(--text-muted, #B8B8B8)' }}>{dark ? 'Dark' : 'Light'}</span>
<div className="toggle-track">
<div className="toggle-knob">
{dark ? <MoonIcon size={10} color="#334155" /> : <SunIcon size={10} color="#ED6D22" />}
</div>
</div>
</button>
</div>
Lives exclusively in the sidebar footer. Never in the top bar, page content, or a dropdown. A Settings page may mirror it as a secondary surface using the same localStorage key (mk-theme).
var(--toggle-track, #E9EAEB). On = var(--brand, #3B9DD4).#ED6D22). Dark mode: moon icon (#334155 — dark slate on white knob, not light gray).left: 3px ↔ left: 21px with transition: left 200ms.role="switch" + aria-checked required. Label ("Light"/"Dark") updates on toggle..toggle-track { width: 40px; height: 22px; border-radius: 11px; background: var(--toggle-track, #E9EAEB); position: relative; }
.toggle-knob { width: 16px; height: 16px; border-radius: 50%; background: #fff; position: absolute; top: 3px; left: 3px; transition: left 200ms; box-shadow: 0 1px 3px rgba(0,0,0,0.35); display: flex; align-items: center; justify-content: center; }
#shell.dark .toggle-knob { left: 21px; }
Every button requires .mk-button base class + a variant class. Missing base class breaks styles.
| Classes | Use |
|---|---|
.mk-button .primary-mk-button | Primary: Save, Submit, Confirm |
.mk-button .secondary-mk-button | Secondary: Cancel, Back |
.mk-button .tertiary-mk-button | Outline/tertiary actions |
.mk-button .quaternary-mk-button | Ghost/minimal actions |
.mk-button .functional-mk-button | Utility: Filter, Sort, Export |
| Class | Height |
|---|---|
| (default) | 30px |
.mk-button-small | 24px |
.mk-button-large | 48px |
.tb-btn {
height: 30px;
padding: 0 16px;
border-radius: 6px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
cursor: pointer;
/* Always use inline-flex for centering — never line-height */
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
font-family: 'Lato', sans-serif;
}
.tb-btn.primary {
background: var(--brand, #0377B3);
color: #ffffff;
border: none;
}
.tb-btn.secondary {
background: transparent;
color: var(--btn-sec-color, #0377B3);
/* 1px border everywhere — same color as text */
border: 1px solid var(--btn-sec-color, #0377B3);
}
/* Dark mode: --btn-sec-color resolves to #7EB8D9 */
opacity: 0.5; pointer-events: none; cursor: not-alloweddisplay: inline-flex; align-items: center; justify-content: center — never line-height for vertical centering// ✅ Correct
<button className="mk-button primary-mk-button">Save Changes</button>
<button className="mk-button secondary-mk-button">Cancel</button>
// ❌ Missing base class
<button className="primary-mk-button">Save</button>
// ❌ Two primary buttons
<button className="mk-button primary-mk-button">Save</button>
<button className="mk-button primary-mk-button">Save & Continue</button>
The myKaarma logo mark background is always #ED6D22 (--primary-accent) in both light and dark mode. The wordmark uses var(--brand, #0377B3) (mode-aware).
// ✅ Logo mark — always orange, both modes
<div style={{
background: '#ED6D22',
borderRadius: 6, width: 28, height: 28,
display: 'flex', alignItems: 'center', justifyContent: 'center'
}}>
<span style={{ color: '#fff', fontSize: 11, fontWeight: 700 }}>mk</span>
</div>
<span style={{ color: 'var(--brand, #0377B3)', fontSize: 13, fontWeight: 700 }}>myKaarma</span>
Never use <button> elements for chips or tab-style filters. The widget host injects its own <button> styles that override custom CSS in light mode. Use <div> with onclick instead.
// ❌ Host styles override this in light mode — appears black
<button className="chip active">All</button>
// ✅ Div is not affected by host button styles
<div className="chip active" onClick={() => setActive('all')}>All</div>
.chip {
height: 28px; padding: 0 14px; border-radius: 14px;
font-size: 11px; font-weight: 700; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
user-select: none;
background: var(--chip-bg, #FFFFFF);
border: 1px solid var(--chip-border, #D8D8D8);
color: var(--chip-text, #535862);
}
.chip.active {
background: var(--chip-active-bg, #D3E4F1);
border-color: var(--chip-active-border, #0377B3);
color: var(--chip-active-text, #0377B3);
}
1px solid var(--border, #E9EAEB), radius 6pxborder-color: var(--brand, #0377B3), box-shadow: 0 0 0 3px rgba(3,119,179,0.3)border-color: #B42318, box-shadow: 0 0 0 3px rgba(180,35,24,0.3)background: var(--disabled-color, #E9EAEB), opacity: 0.5, pointer-events: noneAlways use semantic color pairs:
// ✅ Success badge
<span style={{
color: 'var(--badge-act-fg, #027A48)',
background: 'var(--badge-act-bg, #ECFDF3)',
borderRadius: 4, padding: '2px 8px', fontSize: 10, fontWeight: 700
}}>Active</span>
// ❌ Wrong — blue for a status
<span style={{ color: 'var(--primary-color, #0377B3)' }}>Active</span>
var(--bg-page) background, .myk-subtitle1, font-weight: 700, uppercasebackground: var(--row-hover)// ✅ UUID handled correctly
<td>
<span title={record.id} style={{ color: 'var(--text-muted, #B8B8B8)', cursor: 'pointer' }}>
{record.id.slice(0, 8)}…
</span>
<CopyIcon size={12} onClick={() => copy(record.id)} />
</td>
<div style={{ textAlign: 'center', padding: 48 }}>
<InboxIcon size={48} style={{ color: 'var(--text-muted, #B8B8B8)' }} />
<h5 className="myk-h5">No conversations yet</h5>
<p className="myk-body2" style={{ color: 'var(--text-muted, #B8B8B8)' }}>
Conversations will appear here once customers reach out.
</p>
<button className="mk-button primary-mk-button">Start a Conversation</button>
</div>
12px. Shadow: var(--shadow-lg).rgba(0,0,0,0.5) light / rgba(0,0,0,0.75) darkEscape to closebackground: var(--bg-base-2, #D8D8D8) with shimmer animationaria-label or adjacent visible text| Size | Use |
|---|---|
| 16px | Inline with text, table action icons |
| 20px | Button icons, form field icons |
| 24px | Standalone icons, nav items |
| 48px | Empty state illustrations |
:focus-visible {
outline: none;
border-color: var(--brand, #0377B3);
box-shadow: 0 0 0 3px rgba(3,119,179,0.3);
}
#shell.dark :focus-visible {
box-shadow: 0 0 0 3px rgba(59,157,212,0.4);
}
| Element | Light | Dark |
|---|---|---|
| Primary button | opacity: 0.9 | opacity: 0.9 |
| Secondary button | background: var(--secondary-color, #D3E4F1) | rgba(255,255,255,0.05) |
| Table row | var(--row-hover, #F0F6FB) | rgba(255,255,255,0.04) |
| Nav item | #F5F7FA | rgba(255,255,255,0.05) |
[disabled], .disabled {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
}