feat: fold in the portable Claude Code, zsh and git config; add a drift check

Added (all now linked from their live locations):
- home_root/.claude/settings.json. The statusLine command now uses $HOME
  instead of a Linux-only absolute path.
- home_root/.claude/bin/ratecheck and home_root/.claude/skills/australis-design.
- home_config/zsh/claude-config-dir.zsh and home_root/.local/bin/claude-config-dir-init,
  the per-repo Claude Code login helper and its seeding script.
- home_root/.gitconfig and home_config/git/ignore.
- home_root/.zshenv, with the cargo env line guarded so boxes without rust start clean.

link-dotfiles --check reports drift without changing anything: tracked files whose
live copy is no longer a link (stow dry-run conflicts) and links into this repo
that dangle. Exits 1 on drift. Verified against a clean baseline and two planted
drifts, a detached .nanorc and a dangling link.

.mailmap maps every earlier identity to Vuong Hoang: the repo-local 'Your Name'
placeholder config (now removed), host-generated emails, and aider suffixes.

CLAUDE.md: the symlink warning now lists every linked file and points at --check.
README: documents what is and isn't tracked under .claude, the edit rule, and --check.
This commit is contained in:
vh
2026-09-24 09:07:16 -07:00
parent 77225b01d6
commit 7b16be02bf
49 changed files with 2675 additions and 4 deletions
+12
View File
@@ -0,0 +1,12 @@
# Canonical author for this repo. Earlier commits carry placeholder or host-generated
# identities (a repo-local "Your Name" config, unset user.email on some hosts, aider
# suffixes); this maps them all to the real author for git log / shortlog / blame.
Vuong Hoang <vh@phasefinal.com> <vh@phasefinal.com>
Vuong Hoang <vh@phasefinal.com> <you@example.com>
Vuong Hoang <vh@phasefinal.com> <lkraven@nh3-dev.phasefinal.com>
Vuong Hoang <vh@phasefinal.com> <lkraven@nh3-ansible.phasefinal.com>
Vuong Hoang <vh@phasefinal.com> <lkraven@ana-ml1.phasefinal.com>
Vuong Hoang <vh@phasefinal.com> <lkraven@Vuongs-Mac-mini.local>
Vuong Hoang <vh@phasefinal.com> <lkraven@Vuongs-MacBook-Pro.local>
Vuong Hoang <vh@phasefinal.com> <lkrav@users.noreply.github.com>
Vuong Hoang <vh@phasefinal.com> <llmuser@ana-ml1.phasefinal.com>
+28 -2
View File
@@ -4,8 +4,8 @@ Personal dotfiles. Configs are organized by where they get linked:
| Folder | Links into | Examples |
| -------------- | ------------ | --------------------------------- |
| `home_root` | `~` | `.zshrc`, `.wezterm.lua`, `.nanorc` |
| `home_config` | `~/.config` | `nvim`, `zellij`, `ghostty`, `aerospace` |
| `home_root` | `~` | `.zshrc`, `.zshenv`, `.gitconfig`, `.claude/`, `.local/bin/` |
| `home_config` | `~/.config` | `nvim`, `zellij`, `ghostty`, `aerospace`, `zsh`, `git` |
| `windows` | Windows-only | `windows-terminal/settings.json` |
| `lk-installers`| n/a | helper scripts to install tooling |
@@ -16,6 +16,7 @@ One-shot, host-aware:
```bash
./link-dotfiles # link only (installs stow if missing)
./link-dotfiles --apps # fresh box: install all CLI tools, then link
./link-dotfiles --check # report drift (detached copies, dangling links); changes nothing
```
It detects the host (macOS vs Linux), installs [`stow`](https://www.gnu.org/software/stow/)
@@ -40,6 +41,31 @@ The zsh prompt and plugins (zinit, Powerlevel10k, autosuggestions,
syntax-highlighting, completions, vi-mode, fzf-tab) **self-install** from
`.zshrc` on first shell start, so they're not part of `install-apps`.
Secrets never live here. `.zshenv` sources `~/.config/secrets/env.sh` if it
exists; that file is per-machine and comes from the vault (`secret`), not git.
## Claude Code (`home_root/.claude`)
Tracked, and linked into `~/.claude`: `CLAUDE.md` (global instructions),
`CONTEXT.md`, `settings.json`, `keybindings.json`, `statusline-command.sh`,
`bin/ratecheck`, and the `australis-design` skill. The per-repo login helper is
`home_config/zsh/claude-config-dir.zsh` together with
`home_root/.local/bin/claude-config-dir-init`.
Deliberately **not** tracked:
- `.credentials.json` and `settings.local.json`, which are ignored.
- `projects/`, the per-project auto-memory and transcripts.
- Tool-managed skills (`graphify`, `synced`).
- The galdrabok skill links, which galdrabok owns.
**Edit the repo path, never the link with `sed -i`.** `sed -i` and other
write-temp-then-rename editors replace the link with a detached copy, and this
repo then silently goes stale. That happened to `CLAUDE.md` and
`statusline-command.sh` in September 2026. Claude Code's Edit tool refuses to
write through a link and names the target, which is the safe behaviour. Claude
Code may also rewrite `settings.json` itself when settings change from inside the
app. After that, or whenever in doubt, run `./link-dotfiles --check`.
## Windows (PowerShell)
`stow` doesn't run natively on Windows, so use the PowerShell equivalent. It
+1
View File
@@ -0,0 +1 @@
**/.claude/settings.local.json
+30
View File
@@ -0,0 +1,30 @@
# Per-repo Claude Code login: a `.claude-config-dir` marker at (or above) the
# cwd names a config dir; while inside that tree CLAUDE_CONFIG_DIR points at
# $HOME/.claude-<name> (or an absolute path if the marker holds one), so
# `claude` and `dev-launch claude` both pick up that login. Leaving the tree
# restores whatever was set before. Sourced from ~/.zshrc.
_claude_config_dir_apply() {
local dir="$PWD" marker="" name=""
while [[ "$dir" != "/" && -n "$dir" ]]; do
if [[ -f "$dir/.claude-config-dir" ]]; then marker="$dir/.claude-config-dir"; break; fi
dir="${dir:h}"
done
if [[ -z "$marker" ]]; then
if [[ -n "${_CLAUDE_CONFIG_DIR_BY_HOOK:-}" ]]; then
if [[ -n "${_CLAUDE_CONFIG_DIR_PREV:-}" ]]; then export CLAUDE_CONFIG_DIR="$_CLAUDE_CONFIG_DIR_PREV"; else unset CLAUDE_CONFIG_DIR; fi
unset _CLAUDE_CONFIG_DIR_BY_HOOK _CLAUDE_CONFIG_DIR_PREV
fi
return
fi
name="$(<"$marker")"; name="${name//[[:space:]]/}"
[[ -z "$name" ]] && return
local target
if [[ "$name" == /* ]]; then target="$name"; else target="$HOME/.claude-$name"; fi
if [[ -z "${_CLAUDE_CONFIG_DIR_BY_HOOK:-}" ]]; then _CLAUDE_CONFIG_DIR_PREV="${CLAUDE_CONFIG_DIR:-}"; fi
_CLAUDE_CONFIG_DIR_BY_HOOK=1
export CLAUDE_CONFIG_DIR="$target"
[[ -d "$target" ]] || print -u2 "claude-config-dir: $target does not exist yet — run: claude-config-dir-init $target"
}
autoload -Uz add-zsh-hook
add-zsh-hook chpwd _claude_config_dir_apply
_claude_config_dir_apply
+5 -2
View File
@@ -12,8 +12,11 @@ use `sed -i` or any other write-temp-then-rename tool on
`~/.claude/CLAUDE.md`:** that replaces the link with a detached copy,
and the dotfiles backup silently goes stale. It happened once already,
around 2026-09-18, and went unnoticed until 2026-09-24. The same
applies to `statusline-command.sh`, `CONTEXT.md` and
`keybindings.json` in `~/.claude`.
applies to every other linked file: in `~/.claude` that is
`settings.json`, `statusline-command.sh`, `CONTEXT.md`,
`keybindings.json`, `bin/ratecheck` and `skills/australis-design`, plus
`~/.zshrc`, `~/.zshenv` and `~/.gitconfig`. `~/dotfiles/link-dotfiles
--check` reports any file that has come loose, and any dangling link.
## Operator identity
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Read the rate-limit window and say whether a consult may be dispatched.
The percentages exist only in the JSON the harness pipes to the statusline; no CLI
subcommand exposes them and nothing else on disk carries them. `~/.claude/statusline-command.sh`
tees them to ~/.claude/rate-limits.json on every render (added 2026-09-05, operator directive);
this reads that file.
Exit codes: 0 GO 1 PAUSE 2 UNKNOWN
⚠ UNKNOWN is NOT a pause. A stale or missing file means the instrument is broken, and a
broken instrument that halts every consult is a self-inflicted outage worse than the
overspend it guards. UNKNOWN proceeds and says so loudly, so the gap is visible rather
than silently protective.
"""
import json, os, sys, time
THRESHOLD = 93.0 # advisory default; a project may set its own and say so
STALE_SECONDS = 900 # the statusline renders constantly in an interactive session
PATH = os.path.join(os.environ.get("CLAUDE_CONFIG_DIR") or os.path.expanduser("~/.claude"), "rate-limits.json")
def main() -> int:
try:
with open(PATH, encoding="utf-8") as fh:
d = json.load(fh)
except FileNotFoundError:
print(f"UNKNOWN no {PATH} — statusline tee missing or never rendered")
return 2
except (OSError, ValueError) as exc:
print(f"UNKNOWN {PATH} unreadable: {exc}")
return 2
five, seven = d.get("five_hour_pct"), d.get("seven_day_pct")
age = time.time() - (d.get("written_at") or 0)
stamp = d.get("written_at_iso", "?")
if five is None and seven is None:
print(f"UNKNOWN both windows null (written {stamp}) — session reported no rate_limits")
return 2
if age > STALE_SECONDS:
print(f"UNKNOWN stale by {age/60:.0f} min (written {stamp}) — "
f"last seen 5h={five} 7d={seven}; the statusline is not rendering")
return 2
worst = max(v for v in (five, seven) if v is not None)
which = "5h" if five == worst else "7d"
line = (f"5h={five if five is not None else '?'}% "
f"7d={seven if seven is not None else '?'}% "
f"(age {age/60:.1f} min, threshold {THRESHOLD}%)")
if worst >= THRESHOLD:
reset = d.get(f"{'five_hour' if which == '5h' else 'seven_day'}_resets_at")
when = (time.strftime("%H:%MZ", time.gmtime(reset)) if reset else "unknown")
print(f"PAUSE {line} — {which} at {worst}% >= {THRESHOLD}%; resets {when}")
return 1
print(f"GO {line}")
return 0
if __name__ == "__main__":
sys.exit(main())
+109
View File
@@ -0,0 +1,109 @@
{
"env": {
"VOR_URL": "http://10.250.50.70:7879",
"DISABLE_AUTOUPDATER": "1"
},
"attribution": {
"commit": "",
"pr": "",
"sessionUrl": false
},
"includeCoAuthoredBy": false,
"permissions": {
"defaultMode": "auto"
},
"model": "opus[1m]",
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "test -f .corviduo-canonicals.toml && python scripts/canonical_drift.py 2>&1 || true"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "printf '%s' '{\"hookSpecificOutput\": {\"hookEventName\": \"UserPromptSubmit\", \"additionalContext\": \"SVOS markers, one line each, first person. FIRST line of the reply: ⟦INTENT⟧ <the goal for this turn, present tense> (append — for <handle> when a peer or althing message drove it). FINAL lines, always all three, in order: ⟦STATE⟧ <where things stand — the outcome, never the goal restated>; ⟦AWAITING⟧ <what the session is waiting on>; ⟦DECIDE⟧ <options awaiting the operator, and the reply body must carry the decision behind them>. When ⟦AWAITING⟧ or ⟦DECIDE⟧ has nothing, its entire payload is the two words Nothing pending — no period, no dash, no words after. If autonomous turns ran since the operator last spoke, add a Since your last turn digest directly after ⟦INTENT⟧: one line per turn, what changed, what it cost, every irreversible or outward-facing effect named.\"}}'"
}
]
}
]
},
"statusLine": {
"type": "command",
"command": "bash \"$HOME/.claude/statusline-command.sh\""
},
"enabledPlugins": {
"frontend-design@claude-plugins-official": true,
"althing@althing": true,
"pyright-lsp@claude-plugins-official": true,
"rust-analyzer-lsp@claude-plugins-official": true
},
"extraKnownMarketplaces": {
"althing": {
"source": {
"source": "directory",
"path": "/home/lkraven/.local/share/althing-plugin"
}
}
},
"feedbackDrafts": "off",
"effortLevel": "xhigh",
"modelSettings": {
"claude-fable-5-1": {
"effortLevel": "high"
},
"claude-opus-5-5": {
"effortLevel": "xhigh"
}
},
"tui": "default",
"skipDangerousModePermissionPrompt": true,
"skipWorkflowUsageWarning": true,
"theme": "dark",
"preferredNotifChannel": "terminal_bell",
"remoteControlAtStartup": false,
"crossSessionInbound": "accept",
"agentPushNotifEnabled": true,
"skipAutoPermissionPrompt": true,
"autoMode": {
"allow": [
"$defaults",
"Bash(uv:*) — declared/standard toolchain command, 219 occurrences in this project, already dependency-adjacent",
"Bash(postbox:*) in this project — althing messaging client, 28 occurrences, routine multi-agent coordination"
],
"environment": [
"### Org-wide",
"**Organization**: Corviduo AI / PhaseFinal, Inc / VastBlue AI — three organizations under operator Vuong Hoang (lkraven); this repo (hamr) is a PhaseFinal-hosted gitea project",
"**Cloud provider(s)**: None configured",
"**Repository visibility**: assume private unless the remote host and repo name indicate otherwise, or a visibility check in the transcript shows public — gh visibility check not queryable (org/repo not derivable from origin remote); gitea.phasefinal.com is a private self-hosted org host, no public-visibility evidence found",
"**Internal sharing / snippet hosting**: The Booth (http://10.100.10.50:8090/) for ephemeral media/results; no auth, LAN/WG-internal only — do not drop secrets there",
"**Secrets management**: Vaultwarden-backed `secret` CLI on nh3-dev (vaultwarden.phasefinal.com) — vault is credential source of truth",
"**Default / protected branches**: not queryable via gh (org/repo not derivable from origin remote) — origin/HEAD unset locally too; treat default branch as unknown, do not assume unprotected",
"**CI/CD deploy targets**: None configured",
"**Network posture**: None configured",
"**Host containment**: None configured — assume ordinary developer machine (nh3-dev) with open internet per CLAUDE.md tooling notes",
"**Source control**: gitea.phasefinal.com:vh/hamr.git (this repo's origin) — no additional orgs configured",
"**Trusted internal domains**: gitea.phasefinal.com (org git host, seen in remotes and contacted hosts)",
"**Trusted cloud buckets**: None configured",
"**Key internal services**: 10.100.50.40 (althing post office, 90x contacted), 10.100.10.50 (nh3-dev / Booth / Playwright host, 19x contacted), 10.100.79.3 (7x contacted, unlabeled)",
"**Internal package registry**: None configured",
"**Sensitive data locations & audiences**: any file or store holding personal data, confidential business data, credentials, regulated data, or similarly sensitive material; preserve exact handles when known and share only with audiences cleared at the [named+specifics] bar; note a redacted unusual .gitignore pattern plus `.env` suggest local secrets/config in this repo not meant for commit",
"**Data retention / declassification**: None configured",
"**Sensitive remote targets**: any namespace, host, or container whose name carries `prod` or `production` as a whole word or name segment",
"**Protected deployment namespaces / environments**: None configured — fall back to the Sensitive remote targets heuristic; k8s namespaces seen in this project's transcripts (`hamr`, `docs`) show no prod marker",
"**Protected IaC scopes**: IAM, RBAC, networking, quota, and node-pool resources; anything whose name or tag carries `prod` or `production` as a whole word or name segment",
"### User-specific",
"**Primary use of Claude Code**: software development — geometric-truth vectorization library (hamr), contract-first TDD workflow",
"**Trusted repo**: /home/lkraven/development/hamr (gitea.phasefinal.com:vh/hamr.git) — this repo and its origin remote only",
"**Org-specific CLIs**: uv (Python, primary), postbox / althing-listen (althing messaging bus), booth (media board helper), park, graphify, secret (Vaultwarden CLI, referenced in global CLAUDE.md but not observed in this project's transcripts)",
"**Routine under vh/ prefix**: gitea.phasefinal.com:vh/* repos (own org namespace) are the operator's own checkouts per global CLAUDE.md, but only this project's repo (hamr) is in scope per the just-this-project setting"
]
}
}
@@ -0,0 +1,162 @@
# Australis Design System
> Cool, calm, terminal-first. A dark color and UI system inspired by the Aurora Australis — the Southern Lights.
Australis is a **comfort-focused dark theme** for technology products. The color core was already defined (16 ANSI-style terminal colors, all cooler than neutral, medium-contrast, perceived LAB lightness 65 base / 80 bright). This system extends that core into a full visual language: typography, spacing, elevation, motion, and component patterns suited to terminals, code editors, dashboards, and developer tooling.
The original color spec lives in `uploads/README.md` — © 2024 Vuong Hoang.
---
## Index
- **`README.md`** — this file (visual + content foundations, iconography)
- **`colors_and_type.css`** — design tokens (CSS custom properties + base element styles). Import this from any HTML.
- **`SKILL.md`** — front-matter for using this as a downloadable Claude Skill
- **`assets/`** — logo (mark + wordmark) — see _Caveats_ below
- **`preview/`** — design-system preview cards (registered for the Design System tab)
- **`ui_kits/terminal/`** — interactive terminal + code-editor surface (the signature use-case)
- `index.html`, `Terminal.jsx`, `Editor.jsx`, `StatusBar.jsx`, `Sidebar.jsx`, `CommandPalette.jsx`
## Sources we were given
- **`uploads/README.md`** — the Australis Dark color theme spec (full palette + rationale)
- _No codebase, Figma, or product screens were supplied._ The "technology stack" framing in the brief is interpreted as **developer tooling** (terminal, editor, CLI dashboard) — that's the surface this system is built for. If a real product exists, send it over and we'll re-skin the UI kit accordingly.
---
## Content Fundamentals
Australis copy reads like good terminal output: **direct, lowercase-leaning, low ceremony**. Information dense, never chatty.
- **Tone** — calm, technical, slightly understated. Confident without exclamation marks. Think `man` page meets a well-lit cabin.
- **Voice** — second-person (`you`) for instructional copy. First-person plural (`we`) only when the system is doing something on behalf of the user (`we'll never share it`). Avoid "I" — there's no persona.
- **Casing** — sentence case for everything: buttons, headings, menu items. The wordmark itself is lowercase: `australis`. Eyebrows and metadata may be UPPERCASE (mono, wide-tracked) — that's the only place caps appear.
- **Punctuation** — periods optional on UI strings of one short sentence. Use em-dashes for asides. Code identifiers, hex codes, and paths go in `monospace`.
- **Numbers + units** — keep tight: `184ms`, `12kb`, `v1.2.0`. Spell out only when prose-natural ("twelve modules").
- **No emoji.** No exclamation marks. No marketing superlatives. Status uses glyphs (`✓ ✗ !`) or icons, not emoji.
- **Vibe** — engineer-grade clarity. The product respects the user's attention.
**Examples**
> ✓ tokens compiled (184ms)
> ! 2 deprecated tokens — see migration.md
> ✗ assets/logo.svg not found
> Use 8+ characters with mixed case.
> Aurora prod · staging · preview
> Deploy · Cancel · Learn more
---
## Visual Foundations
### Palette philosophy
- Three families: **Ice** (surface neutrals, cool-tinted), **Aurora** (primary blue/cyan/green — use generously, in that preference order), **Dawn** (red/yellow/magenta accents — use sparingly).
- Neutral scale shifts from cool blue toward green as it brightens. This is the signature — don't flatten it.
- Black = `#222531`, never pure `#000`. White = `#a9bcc3`, never pure `#fff`. The eye sits in low-contrast cool grey; emphasis is achieved by *brightness*, not saturation.
- Semantic mapping: info → blue, success → green, warning → yellow, danger → red. Magenta is reserved for keywords / brand moments.
### Backgrounds
- **Always dark first.** No light mode in this system.
- Solid fills only on chrome — no full-bleed photography, no decorative gradients.
- One sanctioned gradient: the **aurora glow**, a vertical `bright-blue → bright-cyan → green` (see `--glow-*` tokens, the logo, and accent edges). Used at low opacity, never as a background fill behind text.
- No textures, no noise, no patterns. The screen is the polar sky — empty, with light *coming through* it.
### Type
- **Display** — Space Grotesk (geometric humanist; -0.015em tracking).
- **UI / Body** — Inter (15px default body, 1.5–1.65 line-height).
- **Mono** — JetBrains Mono (terminal, code, eyebrows, metadata, hex codes).
- Eyebrows and metadata are **mono uppercase, 11px, 0.08em tracked** — this is one of the system's signatures. Use them often.
- Min sizes: 11px on UI metadata; 13px on body in compact density; 15px default. Never below 11px.
### Spacing & layout
- 4px base. Use the `--sp-*` scale; don't invent in-between values.
- Information density is **medium-high**, especially in chrome (toolbars, sidebars). Marketing surfaces breathe more.
- Containers: `sm 640 / md 880 / lg 1200 / xl 1440`. Center on canvas; no full-bleed marketing layouts.
### Borders
- Default border `--border-default` (`#565f69`) at 1px. Subtle dividers use `--border-subtle` (`#414751`).
- **Border > shadow** for grouping in chrome. Cards layer one subtle shadow on top of a 1px border.
- Featured cards use a 2px **top edge** in `--aus-blue` instead of changing the fill — this is the only sanctioned "accent border" pattern. Never colored *left* borders (the LLM-slop trope).
### Corners
- `--radius-sm` (4px) for inputs, badges, small chrome.
- `--radius-md` (6px) for buttons, tags.
- `--radius-lg` (10px) for cards, panels.
- `--radius-pill` only for status badges and toggle switches.
- Terminal/editor surfaces stay at `--radius-md` or `--radius-lg`. The inner code area itself is square.
### Shadows / elevation
- Shadows are tinted with `rgba(10, 12, 18, *)` — cooler than pure black, matching the bg.
- 4 elevation steps. Most chrome lives at `shadow-1` or `shadow-2`. Modals at `shadow-3`. Heavy popovers at `shadow-4`.
- **Aurora glow** (`--glow-blue/cyan/green`): used as a 3px focus ring or a hover lift on primary buttons. This is the system's signature interaction motif.
- **Inset shadow** (`--shadow-inset`) for terminal wells and sunken inputs — subtle highlight on top edge + dark inner border.
### States
- **Hover** — shift one step *brighter* on neutrals (e.g. `bright-black → dark-30`). Primary buttons: `blue → bright-blue`. Never lower opacity to indicate hover.
- **Active / pressed** — slight downward shift (`translateY(1px)`) + a step *darker* on neutrals, or a desaturated primary.
- **Focus-visible** — 3px aurora glow ring (`--glow-blue` by default; `--glow-green` for success controls).
- **Disabled** — `opacity` is fine here; combine with `cursor: not-allowed`.
### Motion
- Calm, never bouncy. No springs, no overshoot.
- `--ease-out` for entries, `--ease-in-out` for state changes, `--ease-aurora` for hero/long transitions.
- Durations: `120ms` for hovers, `200ms` for state, `320ms` for panels, `800ms` for hero.
- Aurora glow may breathe on the brand mark (8s slow opacity oscillation) — this is the only continuous animation sanctioned by default.
### Transparency & blur
- Glass surfaces (modals, popovers over content) use `--bg-glass` (`rgba(55,59,70,0.55)`) with `backdrop-filter: blur(16px)`. Cap at 16px blur — anything more turns muddy on cool palettes.
- Scrims use `--bg-overlay` (`rgba(34,37,49,0.72)`) — solid feel, no blur.
- Otherwise, opacity is for disabled states only.
### Imagery
- Black-and-white or cool-tinted photography. No warm-tinted hero imagery — it fights the system.
- Reserved use; this is a chrome-first system, not an editorial one.
### Layout fixtures
- Top chrome: 44–52px tall (status bar / tab bar).
- Sidebar: 240–280px (collapsible to 56px icon rail).
- Status bar: 24–28px (mono, 11px).
- Hit targets: 32px min in dense chrome, 40px+ in marketing/settings.
---
## Iconography
Australis uses **[Lucide](https://lucide.dev)** — a clean, line-first icon set that matches the system's pen-stroke restraint.
- **Stroke weight** — `1.75` (slightly heavier than Lucide default for legibility on dark).
- **Default size** — `22px` in chrome, `16px` in dense lists.
- **Color** — `currentColor`. Always inherit from the surrounding text (use `--fg-1` or `--fg-2`).
- **Style** — line, never filled. Two-tone is forbidden.
- **No emoji** anywhere in the UI. Status uses Lucide icons (`check-circle`, `x-circle`, `alert-triangle`, `info`) or mono glyphs (`✓ ✗ ! ➜`).
- **Unicode glyphs** — sanctioned for terminal/CLI surfaces only: `➜ ✓ ✗ ! ⚡ ●`. They feel native there. Outside the terminal, use Lucide.
### Where to get them
The CDN is the simplest path: `<script src="https://unpkg.com/lucide@latest"></script>` and `lucide.createIcons()`. For static HTML cards (preview surfaces), inline the SVG with `stroke-width="1.75" stroke="currentColor" fill="none"`. The `preview/brand-icons.html` card shows 16 hand-inlined examples in the right shape.
If you need a glyph Lucide doesn't have, draw it with the same constraints: 24×24 viewBox, 1.75 stroke, rounded caps + joins, no fill.
---
## Caveats
- **Logo** — no real logo was provided. The `assets/logo-mark.svg` and `assets/logo-wordmark.svg` are interpretive placeholders (an aurora curtain over a horizon) sized to the system. **Replace with the real mark** when available, or tell us the brand voice and we'll commission one.
- **Type stack** — Space Grotesk / Inter / JetBrains Mono is **canonical**. Loaded from Google Fonts at the top of `colors_and_type.css`; exposed via `--font-display`, `--font-sans`, `--font-mono`; documented in _Visual Foundations → Type_.
- **Product surface** — no codebase or screens were attached. The UI kit (`ui_kits/terminal/`) reflects the *kind* of product Australis is built for (a developer tool); it's not a recreation of an existing product.
@@ -0,0 +1,13 @@
---
name: australis-design
description: Use this skill to generate well-branded interfaces and assets for Australis, either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, assets, and UI kit components for prototyping.
user-invocable: true
---
Read the README.md file within this skill, and explore the other available files (especially `colors_and_type.css` for design tokens, `assets/` for the logo, `ui_kits/terminal/` for component recipes, and `preview/` for visual references).
If creating visual artifacts (slides, mocks, throwaway prototypes, etc), copy assets out and create static HTML files for the user to view. Always import `colors_and_type.css` rather than reinventing tokens.
If working on production code, you can copy assets and read the rules here to become an expert in designing with this brand.
If the user invokes this skill without any other guidance, ask them what they want to build or design, ask some questions, and act as an expert designer who outputs HTML artifacts _or_ production code, depending on the need.
@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" fill="none">
<defs>
<linearGradient id="a" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#a4c4ff"></stop>
<stop offset="45%" stop-color="#42dcd1"></stop>
<stop offset="100%" stop-color="#16b866"></stop>
</linearGradient>
</defs>
<rect x="6" y="48" width="52" height="2" rx="1" fill="#6e7882"></rect>
<path d="M10 48 C 14 22, 22 12, 28 8 L 30 8 C 26 18, 22 32, 22 48 Z" fill="url(#a)" opacity="0.95"></path>
<path d="M24 48 C 30 28, 36 18, 42 12 L 44 12 C 38 22, 34 34, 34 48 Z" fill="url(#a)" opacity="0.7"></path>
<path d="M38 48 C 44 32, 50 24, 56 20 L 58 20 C 52 28, 48 38, 48 48 Z" fill="url(#a)" opacity="0.5"></path>
<circle cx="50" cy="14" r="1.5" fill="#cce7ec"></circle>
<circle cx="46" cy="22" r="1" fill="#cce7ec" opacity="0.7"></circle>
<circle cx="54" cy="26" r="0.8" fill="#cce7ec" opacity="0.5"></circle>
</svg>

After

Width:  |  Height:  |  Size: 978 B

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 64" width="280" height="64" fill="none">
<defs>
<linearGradient id="lg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#a4c4ff"></stop>
<stop offset="45%" stop-color="#42dcd1"></stop>
<stop offset="100%" stop-color="#16b866"></stop>
</linearGradient>
</defs>
<g transform="translate(0,0)">
<rect x="6" y="48" width="52" height="2" rx="1" fill="#6e7882"></rect>
<path d="M10 48 C 14 22, 22 12, 28 8 L 30 8 C 26 18, 22 32, 22 48 Z" fill="url(#lg)" opacity="0.95"></path>
<path d="M24 48 C 30 28, 36 18, 42 12 L 44 12 C 38 22, 34 34, 34 48 Z" fill="url(#lg)" opacity="0.7"></path>
<path d="M38 48 C 44 32, 50 24, 56 20 L 58 20 C 52 28, 48 38, 48 48 Z" fill="url(#lg)" opacity="0.5"></path>
<circle cx="50" cy="14" r="1.5" fill="#cce7ec"></circle>
</g>
<text x="76" y="42" font-family="Space Grotesk, Inter, sans-serif" font-size="28" font-weight="600" fill="#cce7ec" letter-spacing="-0.5">australis</text>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,348 @@
/* ==========================================================================
Australis Design System — Tokens
Color theme inspired by the Aurora Australis (Southern Lights).
Cool-tinted, medium-contrast, dark-first.
========================================================================== */
/* -------- Webfonts (Google Fonts fallback — see README "Type") ------------ */
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&display=swap');
:root {
/* ====================================================================
COLOR — RAW PALETTE (from Australis Dark spec)
All colors cooler than neutral. ANSI base = LAB 65, bright = LAB 80.
==================================================================== */
/* Ice — Main / Surface */
--aus-black: #222531; /* background — LAB 15, slightly cool */
--aus-white: #a9bcc3; /* foreground — LAB 75 */
--aus-bright-white: #cce7ec; /* highlight — LAB 90 */
/* Sea — Neutral darks (low-contrast surfaces, dividers, comments) */
--aus-bright-black: #373b46; /* LAB 25 */
--aus-dark-30: #414751; /* LAB 30 */
--aus-dark-40: #565f69; /* LAB 40 */
--aus-dark-50: #6e7882; /* LAB 50 */
--aus-dark-60: #86929d; /* LAB 60 */
/* Sea — Neutral brights (hi-contrast text, emphasis) */
--aus-bright-70: #9daeb6; /* LAB 70 */
--aus-bright-80: #b3cbcf; /* LAB 80 */
/* Aurora — Primary (use generously; preference order: blue → cyan → green) */
--aus-blue: #6388d8;
--aus-bright-blue: #a4c4ff;
--aus-cyan: #00b1a8;
--aus-bright-cyan: #42dcd1;
--aus-green: #16b866;
--aus-bright-green: #51e08a;
/* Dawn — Accents (use sparingly) */
--aus-red: #ff491a;
--aus-bright-red: #ff854f;
--aus-yellow: #e1c631;
--aus-bright-yellow: #ffe14e;
--aus-magenta: #9d78ff;
--aus-bright-magenta: #d8adff;
/* ====================================================================
SEMANTIC — SURFACE / FOREGROUND
==================================================================== */
--bg-0: var(--aus-black); /* page / canvas */
--bg-1: var(--aus-bright-black); /* card / panel */
--bg-2: var(--aus-dark-30); /* hover / nested */
--bg-3: var(--aus-dark-40); /* active / selected */
--bg-overlay: rgba(34, 37, 49, 0.72); /* scrim, modal backdrop */
--bg-glass: rgba(55, 59, 70, 0.55); /* blurred glass surface */
--border-subtle: var(--aus-dark-30);
--border-default: var(--aus-dark-40);
--border-strong: var(--aus-dark-50);
--border-focus: var(--aus-blue);
--fg-0: var(--aus-bright-white); /* primary heading / emphasis */
--fg-1: var(--aus-white); /* body text default */
--fg-2: var(--aus-bright-70); /* secondary text */
--fg-3: var(--aus-dark-60); /* tertiary / metadata */
--fg-muted: var(--aus-dark-50); /* disabled / placeholder */
--fg-comment: var(--aus-dark-50); /* code comments, low-emphasis */
/* On-color foregrounds — use against accent fills */
--fg-on-blue: var(--aus-black);
--fg-on-cyan: var(--aus-black);
--fg-on-green: var(--aus-black);
--fg-on-yellow: var(--aus-black);
--fg-on-red: var(--aus-black);
--fg-on-magenta: var(--aus-black);
/* ====================================================================
SEMANTIC — STATUS / FEEDBACK
==================================================================== */
--status-info: var(--aus-blue);
--status-info-bg: rgba(99, 136, 216, 0.12);
--status-success: var(--aus-green);
--status-success-bg: rgba(22, 184, 102, 0.14);
--status-warning: var(--aus-yellow);
--status-warning-bg: rgba(225, 198, 49, 0.14);
--status-danger: var(--aus-red);
--status-danger-bg: rgba(255, 73, 26, 0.14);
/* Syntax highlighting (canonical mapping) */
--syn-keyword: var(--aus-magenta); /* keywords, control flow */
--syn-function: var(--aus-blue); /* function names, types */
--syn-string: var(--aus-green);
--syn-number: var(--aus-bright-cyan);
--syn-constant: var(--aus-yellow);
--syn-tag: var(--aus-cyan);
--syn-comment: var(--aus-dark-50);
--syn-error: var(--aus-red);
--syn-variable: var(--aus-white);
--syn-operator: var(--aus-bright-70);
/* ====================================================================
TYPE — FAMILIES
Display: Space Grotesk (geometric, slightly humanist — matches the
polar-aurora feel without being sterile).
UI/Body: Inter (workhorse, optimized for screens).
Mono: JetBrains Mono (developer-focused, hinted for terminals).
==================================================================== */
--font-display: "Space Grotesk", "Inter", ui-sans-serif, system-ui, sans-serif;
--font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
/* ====================================================================
TYPE — SCALE (8px-grid friendly, slightly tightened)
==================================================================== */
--fs-3xs: 11px;
--fs-2xs: 12px;
--fs-xs: 13px;
--fs-sm: 14px;
--fs-md: 15px; /* body default */
--fs-lg: 17px;
--fs-xl: 20px;
--fs-2xl: 24px;
--fs-3xl: 30px;
--fs-4xl: 38px;
--fs-5xl: 48px;
--fs-6xl: 64px;
--lh-tight: 1.15;
--lh-snug: 1.3;
--lh-normal: 1.5;
--lh-relaxed: 1.65;
--fw-regular: 400;
--fw-medium: 500;
--fw-semibold: 600;
--fw-bold: 700;
--tracking-tight: -0.015em;
--tracking-normal: 0;
--tracking-wide: 0.04em;
--tracking-caps: 0.08em; /* uppercase eyebrows */
/* ====================================================================
SPACING — 4px base
==================================================================== */
--sp-0: 0px;
--sp-1: 4px;
--sp-2: 8px;
--sp-3: 12px;
--sp-4: 16px;
--sp-5: 20px;
--sp-6: 24px;
--sp-7: 32px;
--sp-8: 40px;
--sp-9: 48px;
--sp-10: 64px;
--sp-11: 80px;
--sp-12: 96px;
/* ====================================================================
RADIUS
Modest rounding. Terminal/CLI surfaces stay sharp; UI surfaces breathe.
==================================================================== */
--radius-none: 0px;
--radius-xs: 2px;
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 10px;
--radius-xl: 14px;
--radius-pill: 9999px;
/* ====================================================================
SHADOW / ELEVATION
Cool-tinted shadows (use the --aus-black hue, not pure black).
==================================================================== */
--shadow-1: 0 1px 2px rgba(10, 12, 18, 0.4);
--shadow-2: 0 2px 6px rgba(10, 12, 18, 0.45), 0 1px 2px rgba(10, 12, 18, 0.4);
--shadow-3: 0 8px 24px rgba(10, 12, 18, 0.5), 0 2px 6px rgba(10, 12, 18, 0.4);
--shadow-4: 0 24px 64px rgba(10, 12, 18, 0.6), 0 8px 16px rgba(10, 12, 18, 0.45);
/* Inner shadow for inset wells (terminal-like) */
--shadow-inset: inset 0 1px 0 rgba(204, 231, 236, 0.04), inset 0 0 0 1px rgba(0,0,0,0.2);
/* Aurora glow — for focus rings and signature accents */
--glow-blue: 0 0 0 3px rgba(99, 136, 216, 0.35);
--glow-cyan: 0 0 0 3px rgba(0, 177, 168, 0.35);
--glow-green: 0 0 0 3px rgba(22, 184, 102, 0.30);
/* ====================================================================
MOTION
Calm, never bouncy. Aurora moves slowly.
==================================================================== */
--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-aurora: cubic-bezier(0.65, 0, 0.35, 1); /* signature slow-glide */
--dur-fast: 120ms;
--dur-base: 200ms;
--dur-slow: 320ms;
--dur-aurora: 800ms; /* hero transitions */
/* ====================================================================
LAYOUT
==================================================================== */
--container-sm: 640px;
--container-md: 880px;
--container-lg: 1200px;
--container-xl: 1440px;
}
/* ==========================================================================
SEMANTIC ELEMENTS — apply tokens to bare HTML
Import this stylesheet and you get sensible defaults out of the box.
========================================================================== */
html, body {
background: var(--bg-0);
color: var(--fg-1);
font-family: var(--font-sans);
font-size: var(--fs-md);
line-height: var(--lh-normal);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
color: var(--fg-0);
font-weight: var(--fw-semibold);
letter-spacing: var(--tracking-tight);
line-height: var(--lh-tight);
margin: 0;
}
h1 { font-size: var(--fs-5xl); }
h2 { font-size: var(--fs-4xl); }
h3 { font-size: var(--fs-2xl); font-weight: var(--fw-medium); }
h4 { font-size: var(--fs-xl); font-weight: var(--fw-medium); }
h5 { font-size: var(--fs-lg); font-weight: var(--fw-medium); }
h6 {
font-size: var(--fs-2xs);
font-weight: var(--fw-semibold);
text-transform: uppercase;
letter-spacing: var(--tracking-caps);
color: var(--fg-2);
font-family: var(--font-sans);
}
p {
font-size: var(--fs-md);
line-height: var(--lh-relaxed);
color: var(--fg-1);
margin: 0 0 var(--sp-4);
text-wrap: pretty;
max-width: 68ch;
}
small, .text-meta {
font-size: var(--fs-2xs);
color: var(--fg-3);
letter-spacing: var(--tracking-wide);
}
a {
color: var(--aus-bright-blue);
text-decoration: none;
border-bottom: 1px solid transparent;
transition: color var(--dur-fast) var(--ease-out),
border-color var(--dur-fast) var(--ease-out);
}
a:hover {
color: var(--aus-bright-cyan);
border-bottom-color: currentColor;
}
a:focus-visible {
outline: none;
box-shadow: var(--glow-blue);
border-radius: var(--radius-xs);
}
code, kbd, samp, pre {
font-family: var(--font-mono);
font-size: 0.92em;
}
code {
background: var(--bg-1);
color: var(--aus-bright-cyan);
padding: 1px 6px;
border-radius: var(--radius-sm);
border: 1px solid var(--border-subtle);
}
pre {
background: var(--bg-1);
color: var(--fg-1);
padding: var(--sp-5);
border-radius: var(--radius-md);
border: 1px solid var(--border-subtle);
overflow-x: auto;
line-height: var(--lh-normal);
}
pre code {
background: transparent;
border: 0;
padding: 0;
color: inherit;
}
kbd {
background: var(--bg-2);
color: var(--fg-0);
border: 1px solid var(--border-default);
border-bottom-width: 2px;
padding: 1px 6px;
border-radius: var(--radius-sm);
font-size: var(--fs-2xs);
box-shadow: 0 1px 0 rgba(0,0,0,0.3);
}
hr {
border: 0;
height: 1px;
background: var(--border-subtle);
margin: var(--sp-7) 0;
}
::selection {
background: rgba(99, 136, 216, 0.35);
color: var(--fg-0);
}
::placeholder {
color: var(--fg-muted);
}
/* Eyebrow / label */
.eyebrow {
font-family: var(--font-mono);
font-size: var(--fs-3xs);
text-transform: uppercase;
letter-spacing: var(--tracking-caps);
color: var(--aus-cyan);
font-weight: var(--fw-medium);
}
@@ -0,0 +1,49 @@
/* Shared shell for preview cards. Cards are 700×variable, dark canvas. */
@import url("../colors_and_type.css");
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: var(--bg-0);
width: 700px;
overflow: hidden;
}
.card {
width: 700px;
padding: 24px;
background: var(--bg-0);
color: var(--fg-1);
font-family: var(--font-sans);
}
.row { display: flex; }
.col { display: flex; flex-direction: column; }
.gap-1 { gap: 4px; } .gap-2 { gap: 8px; } .gap-3 { gap: 12px; }
.gap-4 { gap: 16px; } .gap-5 { gap: 20px; } .gap-6 { gap: 24px; }
.swatch {
border-radius: var(--radius-sm);
border: 1px solid rgba(255,255,255,0.04);
}
.swatch-label {
font-family: var(--font-mono);
font-size: 11px;
color: var(--fg-2);
letter-spacing: 0.02em;
}
.swatch-name {
font-family: var(--font-sans);
font-size: 12px;
color: var(--fg-0);
font-weight: 500;
}
.muted {
color: var(--fg-3);
font-size: 11px;
font-family: var(--font-mono);
letter-spacing: 0.04em;
text-transform: uppercase;
}
@@ -0,0 +1,37 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Iconography (Lucide)</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 12px; padding: 4px 0; }
.ic {
display: flex; flex-direction: column; align-items: center; gap: 6px;
color: var(--fg-1);
}
.ic svg { width: 22px; height: 22px; stroke: currentColor; stroke-width: 1.75; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.ic .nm { font-family: var(--font-mono); font-size: 10px; color: var(--fg-3); letter-spacing: 0.02em; }
.legend { font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); margin-top: 12px; letter-spacing:0.04em; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="ic"><svg viewBox="0 0 24 24"><path d="M3 12h18M12 3v18"/></svg><span class="nm">plus</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M3 12l6 6 12-12"/></svg><span class="nm">check</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5"/></svg><span class="nm">search</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M3 6h18M3 12h18M3 18h18"/></svg><span class="nm">menu</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M12 3v12m0 0l-4-4m4 4l4-4M5 21h14"/></svg><span class="nm">download</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1A2 2 0 1 1 4.3 17l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.3-1.8l-.1-.1A2 2 0 1 1 7 4.3l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg><span class="nm">settings</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8z"/></svg><span class="nm">message</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg><span class="nm">user</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></svg><span class="nm">file</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg><span class="nm">folder</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M4 17l6-6-6-6M12 19h8"/></svg><span class="nm">terminal</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/></svg><span class="nm">code</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg><span class="nm">bell</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg><span class="nm">clock</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M12 22s-8-4.5-8-11.8A5.2 5.2 0 0 1 9.2 5a5 5 0 0 1 2.8.9A5 5 0 0 1 14.8 5 5.2 5.2 0 0 1 20 10.2C20 17.5 12 22 12 22z"/></svg><span class="nm">heart</span></div>
<div class="ic"><svg viewBox="0 0 24 24"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg><span class="nm">bookmark</span></div>
</div>
<div class="legend">Lucide · 1.75 stroke · 22px · current color · use sparingly, line over fill</div>
</div>
</body></html>
@@ -0,0 +1,24 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Logo</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display:grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.pl { display:flex; align-items:center; justify-content:center; padding: 18px; border-radius: 10px; height: 100px; }
.dark { background: #222531; border: 1px solid #373b46; }
.light { background: #cce7ec; }
.meta { display:flex; justify-content:space-between; font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); margin-top:8px; letter-spacing:0.04em; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div>
<div class="pl dark"><img src="../assets/logo-wordmark.svg" alt="Australis" height="46"/></div>
<div class="meta"><span>logo-wordmark.svg</span><span>on bg-0</span></div>
</div>
<div>
<div class="pl light"><img src="../assets/logo-mark.svg" alt="Australis mark" height="58"/></div>
<div class="meta"><span>logo-mark.svg</span><span>on bright-white</span></div>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,25 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Syntax Mapping</title>
<link rel="stylesheet" href="_card.css">
<style>
table { width:100%; border-collapse:collapse; }
td { padding: 5px 0; vertical-align: middle; font-family: var(--font-mono); font-size: 12.5px; }
td.tok { width: 30%; color: var(--fg-3); font-size: 11px; letter-spacing:0.04em; }
td.role { width: 26%; color: var(--fg-2); font-size: 11px; }
td.spec { font-weight: 500; }
td.hex { color: var(--fg-3); font-size: 10.5px; text-align:right; width: 80px; }
</style>
</head><body>
<div class="card">
<table>
<tr><td class="tok">--syn-keyword</td><td class="role">keyword</td><td class="spec" style="color:#9d78ff;">const · return · if</td><td class="hex">#9d78ff</td></tr>
<tr><td class="tok">--syn-function</td><td class="role">function · type</td><td class="spec" style="color:#6388d8;">render() · paint()</td><td class="hex">#6388d8</td></tr>
<tr><td class="tok">--syn-string</td><td class="role">string</td><td class="spec" style="color:#16b866;">'australis'</td><td class="hex">#16b866</td></tr>
<tr><td class="tok">--syn-number</td><td class="role">number</td><td class="spec" style="color:#42dcd1;">-77.85 · 0xff</td><td class="hex">#42dcd1</td></tr>
<tr><td class="tok">--syn-constant</td><td class="role">constant</td><td class="spec" style="color:#e1c631;">LATITUDE · PI</td><td class="hex">#e1c631</td></tr>
<tr><td class="tok">--syn-tag</td><td class="role">tag · attribute</td><td class="spec" style="color:#00b1a8;">&lt;section&gt;</td><td class="hex">#00b1a8</td></tr>
<tr><td class="tok">--syn-comment</td><td class="role">comment</td><td class="spec" style="color:#6e7882;">// note this</td><td class="hex">#6e7882</td></tr>
<tr><td class="tok">--syn-error</td><td class="role">error · diff-del</td><td class="spec" style="color:#ff491a;">undefined</td><td class="hex">#ff491a</td></tr>
</table>
</div>
</body></html>
@@ -0,0 +1,23 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Aurora — Primary Colors</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display:grid; grid-template-columns: repeat(6, 1fr); gap:8px; }
.sw { border-radius:6px; padding:10px; height:120px; display:flex; flex-direction:column; justify-content:space-between; }
.sw .name { font-weight:600; font-size:12px; color:#222531; }
.sw .hex { font-family: var(--font-mono); font-size:10.5px; color:#222531; opacity:0.78; }
h6 { margin: 0 0 12px; }
</style>
</head><body>
<div class="card">
<h6>Aurora · primary · use generously · order: blue → cyan → green</h6>
<div class="grid">
<div class="sw" style="background:#6388d8;"><div class="name">Blue</div><div class="hex">#6388d8</div></div>
<div class="sw" style="background:#a4c4ff;"><div class="name">Bright Blue</div><div class="hex">#a4c4ff</div></div>
<div class="sw" style="background:#00b1a8;"><div class="name">Cyan</div><div class="hex">#00b1a8</div></div>
<div class="sw" style="background:#42dcd1;"><div class="name">Bright Cyan</div><div class="hex">#42dcd1</div></div>
<div class="sw" style="background:#16b866;"><div class="name">Green</div><div class="hex">#16b866</div></div>
<div class="sw" style="background:#51e08a;"><div class="name">Bright Green</div><div class="hex">#51e08a</div></div>
</div>
</div>
</body></html>
@@ -0,0 +1,23 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Dawn — Accent Colors</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display:grid; grid-template-columns: repeat(6, 1fr); gap:8px; }
.sw { border-radius:6px; padding:10px; height:120px; display:flex; flex-direction:column; justify-content:space-between; }
.sw .name { font-weight:600; font-size:12px; color:#222531; }
.sw .hex { font-family: var(--font-mono); font-size:10.5px; color:#222531; opacity:0.78; }
h6 { margin: 0 0 12px; }
</style>
</head><body>
<div class="card">
<h6>Dawn · accent · use sparingly</h6>
<div class="grid">
<div class="sw" style="background:#ff491a;"><div class="name">Red</div><div class="hex">#ff491a</div></div>
<div class="sw" style="background:#ff854f;"><div class="name">Bright Red</div><div class="hex">#ff854f</div></div>
<div class="sw" style="background:#e1c631;"><div class="name">Yellow</div><div class="hex">#e1c631</div></div>
<div class="sw" style="background:#ffe14e;"><div class="name">Bright Yellow</div><div class="hex">#ffe14e</div></div>
<div class="sw" style="background:#9d78ff;"><div class="name">Magenta</div><div class="hex">#9d78ff</div></div>
<div class="sw" style="background:#d8adff;"><div class="name">Bright Magenta</div><div class="hex">#d8adff</div></div>
</div>
</div>
</body></html>
@@ -0,0 +1,35 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Ice — Main Surface Colors</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display:grid; grid-template-columns: 1fr 1fr 1fr; gap:12px; }
.ice {
height: 110px;
border-radius: 8px;
padding: 12px;
display: flex; flex-direction: column; justify-content: space-between;
border: 1px solid rgba(255,255,255,0.06);
}
.ice .top { display:flex; justify-content:space-between; align-items:flex-start; }
.ice .name { font-weight:600; font-size:13px; }
.ice .role { font-family: var(--font-mono); font-size:10px; letter-spacing:0.06em; text-transform:uppercase; opacity:0.7; }
.ice .hex { font-family: var(--font-mono); font-size:12px; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="ice" style="background:#222531; color:#cce7ec;">
<div class="top"><span class="name">Black</span><span class="role">bg</span></div>
<div class="hex">#222531</div>
</div>
<div class="ice" style="background:#a9bcc3; color:#222531;">
<div class="top"><span class="name">White</span><span class="role">fg</span></div>
<div class="hex">#a9bcc3</div>
</div>
<div class="ice" style="background:#cce7ec; color:#222531;">
<div class="top"><span class="name">Bright White</span><span class="role">highlight</span></div>
<div class="hex">#cce7ec</div>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,29 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Sea — Neutral Scale</title>
<link rel="stylesheet" href="_card.css">
<style>
.scale { display:flex; border-radius:8px; overflow:hidden; height:84px; border:1px solid rgba(255,255,255,0.05); }
.step { flex:1; display:flex; flex-direction:column; justify-content:flex-end; padding:8px; }
.step .lab { font-family:var(--font-mono); font-size:10px; opacity:0.8; }
.step .hex { font-family:var(--font-mono); font-size:10px; font-weight:600; }
h6 { margin: 0 0 10px; }
.legend { display:flex; justify-content:space-between; margin-top:8px; font-family:var(--font-mono); font-size:10px; color:var(--fg-3); letter-spacing:0.05em; text-transform:uppercase; }
</style>
</head><body>
<div class="card">
<h6>Sea · neutral scale · cool blue → green tint as it brightens</h6>
<div class="scale">
<div class="step" style="background:#222531;color:#cce7ec;"><div class="lab">L15</div><div class="hex">#222531</div></div>
<div class="step" style="background:#373b46;color:#cce7ec;"><div class="lab">L25</div><div class="hex">#373b46</div></div>
<div class="step" style="background:#414751;color:#cce7ec;"><div class="lab">L30</div><div class="hex">#414751</div></div>
<div class="step" style="background:#565f69;color:#cce7ec;"><div class="lab">L40</div><div class="hex">#565f69</div></div>
<div class="step" style="background:#6e7882;color:#222531;"><div class="lab">L50</div><div class="hex">#6e7882</div></div>
<div class="step" style="background:#86929d;color:#222531;"><div class="lab">L60</div><div class="hex">#86929d</div></div>
<div class="step" style="background:#9daeb6;color:#222531;"><div class="lab">L70</div><div class="hex">#9daeb6</div></div>
<div class="step" style="background:#a9bcc3;color:#222531;"><div class="lab">L75</div><div class="hex">#a9bcc3</div></div>
<div class="step" style="background:#b3cbcf;color:#222531;"><div class="lab">L80</div><div class="hex">#b3cbcf</div></div>
<div class="step" style="background:#cce7ec;color:#222531;"><div class="lab">L90</div><div class="hex">#cce7ec</div></div>
</div>
<div class="legend"><span>Black</span><span>Dark 30 / 40 / 50 / 60</span><span>Bright 70 / 80 / 90</span></div>
</div>
</body></html>
@@ -0,0 +1,40 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Semantic / Status Colors</title>
<link rel="stylesheet" href="_card.css">
<style>
.row-s { display:flex; gap:10px; margin-bottom: 10px; }
.pill {
flex: 1;
display: flex; align-items:center; gap:10px;
padding: 10px 14px;
border-radius: 8px;
font-family: var(--font-sans);
font-size: 13px;
font-weight: 500;
border: 1px solid;
}
.pill .dot { width: 8px; height:8px; border-radius:50%; }
.pill .label { flex: 1; }
.pill .hex { font-family: var(--font-mono); font-size:11px; opacity:0.85; }
.info { background: rgba(99,136,216,0.12); border-color: rgba(99,136,216,0.4); color:#a4c4ff; }
.info .dot { background:#6388d8; }
.success { background: rgba(22,184,102,0.14); border-color: rgba(22,184,102,0.4); color:#51e08a; }
.success .dot{ background:#16b866; }
.warning { background: rgba(225,198,49,0.14); border-color: rgba(225,198,49,0.4); color:#ffe14e; }
.warning .dot{ background:#e1c631; }
.danger { background: rgba(255,73,26,0.14); border-color: rgba(255,73,26,0.4); color:#ff854f; }
.danger .dot { background:#ff491a; }
</style>
</head><body>
<div class="card">
<div class="row-s">
<div class="pill info"><span class="dot"></span><span class="label">Info — informational</span><span class="hex">--status-info</span></div>
<div class="pill success"><span class="dot"></span><span class="label">Success — operation complete</span><span class="hex">--status-success</span></div>
</div>
<div class="row-s">
<div class="pill warning"><span class="dot"></span><span class="label">Warning — review needed</span><span class="hex">--status-warning</span></div>
<div class="pill danger"><span class="dot"></span><span class="label">Danger — destructive / error</span><span class="hex">--status-danger</span></div>
</div>
</div>
</body></html>
@@ -0,0 +1,51 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Badges & Tags</title>
<link rel="stylesheet" href="_card.css">
<style>
.row-b { display:flex; gap:8px; align-items:center; margin-bottom:12px; flex-wrap:wrap; }
.row-b:last-child { margin-bottom: 0; }
.label { font-family: var(--font-mono); font-size:10px; color: var(--fg-3); width: 80px; letter-spacing:0.06em; text-transform:uppercase; }
.badge {
display:inline-flex; align-items:center; gap:6px;
font-family: var(--font-sans); font-size: 11.5px; font-weight: 500;
padding: 3px 9px; border-radius: 9999px;
border: 1px solid;
line-height: 1.4;
}
.dot { width:6px; height:6px; border-radius:50%; }
.b-blue { background: rgba(99,136,216,0.14); color:#a4c4ff; border-color: rgba(99,136,216,0.4); }
.b-green { background: rgba(22,184,102,0.14); color:#51e08a; border-color: rgba(22,184,102,0.4); }
.b-yellow { background: rgba(225,198,49,0.14); color:#ffe14e; border-color: rgba(225,198,49,0.4); }
.b-red { background: rgba(255,73,26,0.14); color:#ff854f; border-color: rgba(255,73,26,0.4); }
.b-mag { background: rgba(157,120,255,0.14); color:#d8adff; border-color: rgba(157,120,255,0.4); }
.b-cyan { background: rgba(0,177,168,0.14); color:#42dcd1; border-color: rgba(0,177,168,0.4); }
.b-neutral{ background: #373b46; color:#a9bcc3; border-color:#565f69; }
.tag { background:#373b46; color:#cce7ec; padding:2px 8px; border-radius:4px; font-family: var(--font-mono); font-size: 11px; border:1px solid #565f69; }
</style>
</head><body>
<div class="card">
<div class="row-b">
<span class="label">Status</span>
<span class="badge b-green"><span class="dot" style="background:#16b866;"></span>Live</span>
<span class="badge b-blue"><span class="dot" style="background:#6388d8;"></span>Deploying</span>
<span class="badge b-yellow"><span class="dot" style="background:#e1c631;"></span>Warning</span>
<span class="badge b-red"><span class="dot" style="background:#ff491a;"></span>Failed</span>
<span class="badge b-neutral"><span class="dot" style="background:#86929d;"></span>Idle</span>
</div>
<div class="row-b">
<span class="label">Counter</span>
<span class="badge b-blue">v1.2.0</span>
<span class="badge b-mag">beta</span>
<span class="badge b-cyan">edge</span>
<span class="badge b-green">+12</span>
</div>
<div class="row-b">
<span class="label">Tag</span>
<span class="tag">typescript</span>
<span class="tag">terminal</span>
<span class="tag">aurora</span>
<span class="tag">v18.3.1</span>
</div>
</div>
</body></html>
@@ -0,0 +1,50 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Buttons</title>
<link rel="stylesheet" href="_card.css">
<style>
.row-b { display:flex; gap:10px; align-items:center; margin-bottom:12px; flex-wrap:wrap; }
.row-b:last-child { margin-bottom:0; }
.label { font-family: var(--font-mono); font-size:10px; color: var(--fg-3); width: 70px; letter-spacing:0.06em; text-transform:uppercase; }
.btn {
font-family: var(--font-sans); font-size: 13px; font-weight: 500;
padding: 8px 16px; border-radius: 6px; border: 1px solid transparent;
cursor: pointer; line-height: 1; letter-spacing: 0.005em;
transition: all 120ms cubic-bezier(.2,.8,.2,1);
display: inline-flex; align-items:center; gap: 8px;
}
.btn-primary { background: #6388d8; color: #222531; }
.btn-primary.hover { background: #a4c4ff; }
.btn-primary.active { background: #4d72c2; transform: translateY(1px); }
.btn-secondary { background: #373b46; color: #cce7ec; border-color: #565f69; }
.btn-secondary.hover { background: #414751; border-color: #6e7882; }
.btn-ghost { background: transparent; color: #a4c4ff; }
.btn-ghost.hover { background: rgba(99,136,216,0.10); }
.btn-danger { background: transparent; color: #ff854f; border-color: rgba(255,73,26,0.4); }
.btn-danger.hover { background: rgba(255,73,26,0.14); }
.btn-disabled { background:#373b46; color:#6e7882; border-color:#414751; cursor:not-allowed; }
.btn-focus { background:#6388d8; color:#222531; box-shadow: 0 0 0 3px rgba(99,136,216,0.35); }
</style>
</head><body>
<div class="card">
<div class="row-b">
<span class="label">Primary</span>
<button class="btn btn-primary">Deploy</button>
<button class="btn btn-primary hover">Deploy</button>
<button class="btn btn-primary active">Deploy</button>
<button class="btn btn-focus">Deploy</button>
</div>
<div class="row-b">
<span class="label">Secondary</span>
<button class="btn btn-secondary">Cancel</button>
<button class="btn btn-secondary hover">Cancel</button>
<button class="btn btn-ghost">Learn more</button>
<button class="btn btn-ghost hover">Learn more</button>
</div>
<div class="row-b">
<span class="label">Status</span>
<button class="btn btn-danger">Delete</button>
<button class="btn btn-danger hover">Delete</button>
<button class="btn btn-disabled">Disabled</button>
</div>
</div>
</body></html>
@@ -0,0 +1,36 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Cards</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.ucard {
background: var(--bg-1);
border: 1px solid var(--border-subtle);
border-radius: 10px;
padding: 16px;
box-shadow: 0 2px 6px rgba(10,12,18,0.45), 0 1px 2px rgba(10,12,18,0.4);
}
.ucard h4 { font-family: var(--font-display); color: var(--fg-0); font-size: 17px; font-weight:600; margin: 0 0 4px; letter-spacing:-0.01em; }
.ucard p { font-size: 13px; color: var(--fg-2); margin: 0 0 12px; line-height: 1.5; }
.ucard .meta { display:flex; justify-content:space-between; align-items:center; font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); }
.ucard .glyph { width:24px; height:24px; border-radius:6px; background: linear-gradient(135deg,#6388d8,#42dcd1); margin-bottom: 12px; }
.feat { border-top: 2px solid #6388d8; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="ucard">
<div class="glyph"></div>
<h4>Standard card</h4>
<p>Default surface for grouping content. Subtle border, level-2 shadow, level-1 background.</p>
<div class="meta"><span>--bg-1</span><span>radius-lg</span></div>
</div>
<div class="ucard feat">
<div class="glyph" style="background: linear-gradient(135deg,#9d78ff,#42dcd1);"></div>
<h4>Featured card</h4>
<p>Add a 2px aurora top-edge to single out a primary item without changing fill.</p>
<div class="meta"><span>border-top: blue</span><span>shadow-2</span></div>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,49 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Toggles & Controls</title>
<link rel="stylesheet" href="_card.css">
<style>
.row-c { display:flex; gap:18px; align-items:center; margin-bottom: 14px; }
.row-c:last-child { margin-bottom:0; }
.label { font-family: var(--font-mono); font-size:10px; color: var(--fg-3); width: 70px; letter-spacing:0.06em; text-transform:uppercase; }
/* checkbox */
.ck { width:16px; height:16px; border-radius:4px; border:1px solid #6e7882; background:#1b1e28; display:inline-flex; align-items:center; justify-content:center; }
.ck.on { background:#6388d8; border-color:#6388d8; color:#222531; font-size:11px; font-weight:700; }
/* radio */
.rd { width:16px; height:16px; border-radius:50%; border:1px solid #6e7882; background:#1b1e28; display:inline-flex; align-items:center; justify-content:center; }
.rd.on::after { content:''; width:8px; height:8px; border-radius:50%; background:#6388d8; }
/* switch */
.sw { width: 32px; height:18px; border-radius:9999px; background:#414751; position:relative; transition:background 200ms; }
.sw::after { content:''; position:absolute; top:2px; left:2px; width:14px; height:14px; border-radius:50%; background:#cce7ec; transition: left 200ms; }
.sw.on { background:#16b866; }
.sw.on::after { left: 16px; }
/* slider */
.sld { width: 200px; height:4px; background:#414751; border-radius:2px; position:relative; }
.sld .fill { position:absolute; left:0; top:0; bottom:0; width:60%; background:linear-gradient(90deg,#6388d8,#42dcd1); border-radius:2px; }
.sld .knob { position:absolute; left:60%; top:50%; transform:translate(-50%,-50%); width:14px; height:14px; border-radius:50%; background:#cce7ec; box-shadow: 0 0 0 3px rgba(99,136,216,0.35); }
.name { font-family: var(--font-sans); font-size: 13px; color: var(--fg-1); }
</style>
</head><body>
<div class="card">
<div class="row-c"><span class="label">Checkbox</span>
<span class="ck"></span><span class="name">Off</span>
<span class="ck on">✓</span><span class="name">On</span>
</div>
<div class="row-c"><span class="label">Radio</span>
<span class="rd"></span><span class="name">Option A</span>
<span class="rd on"></span><span class="name">Option B</span>
</div>
<div class="row-c"><span class="label">Switch</span>
<span class="sw"></span><span class="name">Off</span>
<span class="sw on"></span><span class="name">On</span>
</div>
<div class="row-c"><span class="label">Slider</span>
<span class="sld"><span class="fill"></span><span class="knob"></span></span>
<span class="name" style="font-family:var(--font-mono); color:#42dcd1;">0.60</span>
</div>
</div>
</body></html>
@@ -0,0 +1,52 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Code Editor</title>
<link rel="stylesheet" href="_card.css">
<style>
.editor {
background: #1b1e28;
border: 1px solid #373b46;
border-radius: 10px;
overflow: hidden;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.65;
box-shadow: 0 8px 24px rgba(10,12,18,0.5);
}
.tabs { background:#222531; display:flex; border-bottom:1px solid #373b46; }
.tab { padding: 8px 14px; font-size: 11.5px; color:#6e7882; border-right:1px solid #373b46; display:flex; align-items:center; gap:8px; }
.tab.active { color:#cce7ec; background:#1b1e28; border-bottom: 1px solid #1b1e28; margin-bottom:-1px; }
.tab .dot { width:6px; height:6px; border-radius:50%; background:#42dcd1; }
.body { display:flex; }
.gutter {
color:#414751; padding: 12px 10px 12px 16px; text-align: right; user-select:none; background:#1b1e28; border-right:1px solid #2a2e38;
font-size: 12.5px;
}
.code { padding: 12px 18px; flex: 1; overflow:hidden; }
.ln { white-space: pre; }
.kw { color:#9d78ff; } .fn { color:#6388d8; } .str { color:#16b866; }
.num { color:#42dcd1; } .com { color:#6e7882; } .var { color:#a9bcc3; }
.ty { color:#42dcd1; } .const { color:#ffe14e; }
</style>
</head><body>
<div class="card">
<div class="editor">
<div class="tabs">
<div class="tab active"><span class="dot"></span>aurora.ts</div>
<div class="tab">tokens.css</div>
<div class="tab">README.md</div>
</div>
<div class="body">
<div class="gutter">1<br>2<br>3<br>4<br>5<br>6<br>7</div>
<div class="code">
<div class="ln"><span class="com">// Render the southern sky</span></div>
<div class="ln"><span class="kw">import</span> { <span class="fn">paint</span> } <span class="kw">from</span> <span class="str">'./aurora'</span>;</div>
<div class="ln"></div>
<div class="ln"><span class="kw">export const</span> <span class="const">LATITUDE</span> = <span class="num">-77.85</span>;</div>
<div class="ln"><span class="kw">export function</span> <span class="fn">render</span>(<span class="var">name</span>: <span class="ty">string</span>) {</div>
<div class="ln">&nbsp;&nbsp;<span class="kw">return</span> <span class="fn">paint</span>(<span class="var">name</span>, <span class="const">LATITUDE</span>);</div>
<div class="ln">}</div>
</div>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,35 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Inputs</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.field { display:flex; flex-direction:column; gap:6px; }
label.lbl { font-family: var(--font-mono); font-size: 10px; color: var(--fg-3); text-transform: uppercase; letter-spacing: 0.08em; }
.input {
background: #1b1e28;
border: 1px solid #414751;
color: #cce7ec;
font-family: var(--font-sans);
font-size: 13px;
padding: 9px 12px;
border-radius: 6px;
outline: none;
box-shadow: inset 0 1px 0 rgba(204,231,236,0.04);
}
.input.focus { border-color: #6388d8; box-shadow: 0 0 0 3px rgba(99,136,216,0.35); }
.input.error { border-color: #ff491a; box-shadow: 0 0 0 3px rgba(255,73,26,0.25); }
.input.disabled { opacity: 0.5; }
.help { font-size: 11px; color: var(--fg-3); font-family: var(--font-sans); }
.help.err { color: #ff854f; }
.input::placeholder { color: #6e7882; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="field"><label class="lbl">Default</label><input class="input" placeholder="user@australis.dev"/><span class="help">We'll never share it.</span></div>
<div class="field"><label class="lbl">Focus</label><input class="input focus" value="aurora-prod"/><span class="help">3 of 32 chars used.</span></div>
<div class="field"><label class="lbl">Error</label><input class="input error" value="!@#weak"/><span class="help err">Use 8+ characters with mixed case.</span></div>
<div class="field"><label class="lbl">Disabled</label><input class="input disabled" value="locked-by-admin" disabled/><span class="help">Inherited from workspace.</span></div>
</div>
</div>
</body></html>
@@ -0,0 +1,47 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Menu & List</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display:grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.menu, .list {
background: var(--bg-1);
border: 1px solid var(--border-subtle);
border-radius: 8px;
padding: 6px;
box-shadow: 0 8px 24px rgba(10,12,18,0.5);
}
.item {
display: flex; align-items: center; justify-content: space-between;
padding: 8px 10px;
border-radius: 5px;
font-family: var(--font-sans); font-size: 13px;
color: var(--fg-1);
}
.item .kbd { font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); background:#1b1e28; padding:2px 6px; border-radius:4px; border:1px solid #414751; }
.item:hover, .item.hover { background: #414751; color: #cce7ec; }
.item.sel { background: rgba(99,136,216,0.18); color: #a4c4ff; }
.item.danger { color: #ff854f; }
.div { height:1px; background: #373b46; margin: 4px 6px; }
.head { font-family: var(--font-mono); font-size: 10px; text-transform:uppercase; letter-spacing:0.08em; color: var(--fg-3); padding: 8px 10px 4px; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="menu">
<div class="head">Workspace</div>
<div class="item sel">Aurora prod <span class="kbd">⌘1</span></div>
<div class="item hover">Staging <span class="kbd">⌘2</span></div>
<div class="item">Preview <span class="kbd">⌘3</span></div>
<div class="div"></div>
<div class="item">Settings <span class="kbd">⌘,</span></div>
<div class="item danger">Sign out</div>
</div>
<div class="list">
<div class="head">Commits — main</div>
<div class="item"><span style="display:flex;gap:8px;align-items:center;"><span style="color:#42dcd1;font-family:var(--font-mono);font-size:11px;">a3f2d</span> fix: token regression</span><span class="kbd">2m</span></div>
<div class="item"><span style="display:flex;gap:8px;align-items:center;"><span style="color:#42dcd1;font-family:var(--font-mono);font-size:11px;">9b1c0</span> feat: aurora glow</span><span class="kbd">14m</span></div>
<div class="item"><span style="display:flex;gap:8px;align-items:center;"><span style="color:#42dcd1;font-family:var(--font-mono);font-size:11px;">52ee7</span> docs: usage rules</span><span class="kbd">1h</span></div>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,62 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Terminal Surface</title>
<link rel="stylesheet" href="_card.css">
<style>
.term {
background: #1b1e28;
border-radius: 10px;
border: 1px solid #373b46;
box-shadow: 0 8px 24px rgba(10,12,18,0.5), 0 2px 6px rgba(10,12,18,0.4);
overflow: hidden;
}
.titlebar {
background: #222531;
padding: 8px 12px;
border-bottom: 1px solid #373b46;
display: flex; align-items: center; gap: 8px;
}
.dots { display:flex; gap:6px; }
.dot { width:11px; height:11px; border-radius:50%; }
.title {
font-family: var(--font-mono);
font-size: 11px;
color: #6e7882;
margin-left: 8px;
letter-spacing: 0.04em;
}
.body {
padding: 14px 18px;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.65;
}
.ln { display: block; }
.p { color:#42dcd1; } /* prompt */
.cmd { color:#cce7ec; }
.com { color:#6e7882; }
.ok { color:#51e08a; }
.info{ color:#a4c4ff; }
.warn{ color:#ffe14e; }
.err { color:#ff854f; }
.mag { color:#9d78ff; }
.cur { display:inline-block; width:8px; height:14px; background:#42dcd1; vertical-align:middle; margin-left:2px; }
</style>
</head><body>
<div class="card">
<div class="term">
<div class="titlebar">
<div class="dots"><span class="dot" style="background:#ff491a;"></span><span class="dot" style="background:#e1c631;"></span><span class="dot" style="background:#16b866;"></span></div>
<span class="title">aurora ~/projects/australis · zsh</span>
</div>
<div class="body">
<span class="ln"><span class="p">➜</span> <span class="cmd">australis</span> <span class="mag">build</span> <span class="info">--watch</span></span>
<span class="ln"><span class="com"># compiling 24 modules…</span></span>
<span class="ln"><span class="ok">✓</span> tokens compiled <span class="com">(184ms)</span></span>
<span class="ln"><span class="ok">✓</span> ui-kit bundled <span class="com">(412ms)</span></span>
<span class="ln"><span class="warn">!</span> 2 deprecated tokens — see migration.md</span>
<span class="ln"><span class="err">✗</span> assets/logo.svg not found</span>
<span class="ln"><span class="p">➜</span> <span class="cur"></span></span>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,27 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Radii</title>
<link rel="stylesheet" href="_card.css">
<style>
.grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 10px; }
.cell { display:flex; flex-direction:column; align-items:center; gap:8px; }
.box {
width: 70px; height: 70px;
background: linear-gradient(135deg, #373b46, #414751);
border: 1px solid var(--border-default);
}
.tok { font-family: var(--font-mono); font-size: 10.5px; color: var(--fg-2); letter-spacing:0.03em; }
.px { font-family: var(--font-mono); font-size: 10px; color: var(--fg-3); }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="cell"><div class="box" style="border-radius:0;"></div><span class="tok">none</span><span class="px">0</span></div>
<div class="cell"><div class="box" style="border-radius:2px;"></div><span class="tok">xs</span><span class="px">2</span></div>
<div class="cell"><div class="box" style="border-radius:4px;"></div><span class="tok">sm</span><span class="px">4</span></div>
<div class="cell"><div class="box" style="border-radius:6px;"></div><span class="tok">md</span><span class="px">6</span></div>
<div class="cell"><div class="box" style="border-radius:10px;"></div><span class="tok">lg</span><span class="px">10</span></div>
<div class="cell"><div class="box" style="border-radius:14px;"></div><span class="tok">xl</span><span class="px">14</span></div>
<div class="cell"><div class="box" style="border-radius:9999px;"></div><span class="tok">pill</span><span class="px">∞</span></div>
</div>
</div>
</body></html>
@@ -0,0 +1,25 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Spacing</title>
<link rel="stylesheet" href="_card.css">
<style>
.row-s { display:flex; align-items:center; gap:10px; padding: 4px 0; }
.bar { background: linear-gradient(90deg, #6388d8, #00b1a8); height: 14px; border-radius: 2px; }
.tok { font-family: var(--font-mono); font-size: 11px; color: var(--fg-2); width: 70px; letter-spacing:0.04em; }
.px { font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); width: 44px; text-align:right; }
</style>
</head><body>
<div class="card">
<div class="row-s"><span class="tok">--sp-1</span> <span class="px">4px</span> <span class="bar" style="width:4px;"></span></div>
<div class="row-s"><span class="tok">--sp-2</span> <span class="px">8px</span> <span class="bar" style="width:8px;"></span></div>
<div class="row-s"><span class="tok">--sp-3</span> <span class="px">12px</span><span class="bar" style="width:12px;"></span></div>
<div class="row-s"><span class="tok">--sp-4</span> <span class="px">16px</span><span class="bar" style="width:16px;"></span></div>
<div class="row-s"><span class="tok">--sp-5</span> <span class="px">20px</span><span class="bar" style="width:20px;"></span></div>
<div class="row-s"><span class="tok">--sp-6</span> <span class="px">24px</span><span class="bar" style="width:24px;"></span></div>
<div class="row-s"><span class="tok">--sp-7</span> <span class="px">32px</span><span class="bar" style="width:32px;"></span></div>
<div class="row-s"><span class="tok">--sp-8</span> <span class="px">40px</span><span class="bar" style="width:40px;"></span></div>
<div class="row-s"><span class="tok">--sp-9</span> <span class="px">48px</span><span class="bar" style="width:48px;"></span></div>
<div class="row-s"><span class="tok">--sp-10</span><span class="px">64px</span><span class="bar" style="width:64px;"></span></div>
<div class="row-s"><span class="tok">--sp-11</span><span class="px">80px</span><span class="bar" style="width:80px;"></span></div>
<div class="row-s"><span class="tok">--sp-12</span><span class="px">96px</span><span class="bar" style="width:96px;"></span></div>
</div>
</body></html>
@@ -0,0 +1,30 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Shadow & Glow</title>
<link rel="stylesheet" href="_card.css">
<style>
body { background: var(--bg-0); }
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; padding-top: 6px; padding-bottom: 6px; }
.cell { display:flex; flex-direction:column; align-items:center; gap:8px; }
.box {
width: 100%; height: 70px;
background: var(--bg-1);
border: 1px solid var(--border-subtle);
border-radius: 8px;
}
.tok { font-family: var(--font-mono); font-size: 10.5px; color: var(--fg-2); letter-spacing:0.03em; }
</style>
</head><body>
<div class="card">
<div class="grid">
<div class="cell"><div class="box" style="box-shadow: 0 1px 2px rgba(10,12,18,0.4);"></div><span class="tok">shadow-1</span></div>
<div class="cell"><div class="box" style="box-shadow: 0 2px 6px rgba(10,12,18,0.45), 0 1px 2px rgba(10,12,18,0.4);"></div><span class="tok">shadow-2</span></div>
<div class="cell"><div class="box" style="box-shadow: 0 8px 24px rgba(10,12,18,0.5), 0 2px 6px rgba(10,12,18,0.4);"></div><span class="tok">shadow-3</span></div>
<div class="cell"><div class="box" style="box-shadow: 0 24px 64px rgba(10,12,18,0.6), 0 8px 16px rgba(10,12,18,0.45);"></div><span class="tok">shadow-4</span></div>
<div class="cell"><div class="box" style="box-shadow: 0 0 0 3px rgba(99,136,216,0.35); border-color:#6388d8;"></div><span class="tok">glow-blue</span></div>
<div class="cell"><div class="box" style="box-shadow: 0 0 0 3px rgba(0,177,168,0.35); border-color:#00b1a8;"></div><span class="tok">glow-cyan</span></div>
<div class="cell"><div class="box" style="box-shadow: 0 0 0 3px rgba(22,184,102,0.30); border-color:#16b866;"></div><span class="tok">glow-green</span></div>
<div class="cell"><div class="box" style="box-shadow: inset 0 1px 0 rgba(204,231,236,0.05), inset 0 0 0 1px rgba(0,0,0,0.3); background:#1b1e28;"></div><span class="tok">inset</span></div>
</div>
</div>
</body></html>
@@ -0,0 +1,22 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Type · Body & UI</title>
<link rel="stylesheet" href="_card.css">
<style>
.stack { display: flex; flex-direction: column; gap: 12px; }
.row { display:flex; align-items:baseline; justify-content:space-between; gap: 16px; }
.meta { font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); white-space:nowrap; letter-spacing:0.04em; }
.lead { font-family: var(--font-sans); color: var(--fg-0); font-size:17px; font-weight:500; line-height:1.45; }
.body { font-family: var(--font-sans); color: var(--fg-1); font-size:15px; line-height:1.6; max-width: 460px; }
.small { font-family: var(--font-sans); color: var(--fg-2); font-size:13px; }
.eye { font-family: var(--font-mono); color: #00b1a8; font-size:11px; text-transform:uppercase; letter-spacing:0.08em; font-weight:500;}
</style>
</head><body>
<div class="card">
<div class="stack">
<div class="row"><span class="eye">// EYEBROW · MONO 11/UC</span><span class="meta">.eyebrow</span></div>
<div class="row"><span class="lead">A southern sky, rendered in pixels.</span><span class="meta">Lead · 17 / 500</span></div>
<div class="row"><span class="body">Body copy is Inter at 15px with a 1.6 line height. It carries longform reading on dashboards, docs, and settings.</span><span class="meta">Body · 15 / 400</span></div>
<div class="row"><span class="small">Secondary text — captions, helper copy, metadata.</span><span class="meta">Small · 13 / 400</span></div>
</div>
</div>
</body></html>
@@ -0,0 +1,27 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Type · Display</title>
<link rel="stylesheet" href="_card.css">
<style>
.stack { display: flex; flex-direction: column; gap: 14px; }
.row { display:flex; align-items:baseline; justify-content:space-between; gap: 16px; }
.specimen { color: var(--fg-0); font-family: var(--font-display); letter-spacing:-0.015em; line-height:1.05; }
.meta { font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); white-space: nowrap; letter-spacing:0.04em; }
</style>
</head><body>
<div class="card">
<div class="stack">
<div class="row">
<span class="specimen" style="font-size:54px; font-weight:600;">Aurora&nbsp;Australis</span>
<span class="meta">Display · 54 / 600</span>
</div>
<div class="row">
<span class="specimen" style="font-size:38px; font-weight:600;">Cool by design.</span>
<span class="meta">H1 · 38 / 600</span>
</div>
<div class="row">
<span class="specimen" style="font-size:24px; font-weight:500;">Section heading</span>
<span class="meta">H3 · 24 / 500</span>
</div>
</div>
</div>
</body></html>
@@ -0,0 +1,31 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Type · Mono & Code</title>
<link rel="stylesheet" href="_card.css">
<style>
.term {
background: var(--bg-1);
border: 1px solid var(--border-subtle);
border-radius: 8px;
padding: 16px 20px;
font-family: var(--font-mono);
font-size: 13.5px;
line-height: 1.6;
color: var(--fg-1);
}
.kw { color:#9d78ff; } .fn { color:#6388d8; } .str { color:#16b866; }
.num { color:#42dcd1; } .com { color:#6e7882; } .var { color:#a9bcc3; }
.tag { color:#00b1a8; } .err { color:#ff491a; }
.prompt { color:#42dcd1; }
.meta { font-family: var(--font-mono); font-size: 11px; color: var(--fg-3); margin-top:10px; letter-spacing:0.04em; display:flex; justify-content:space-between; }
</style>
</head><body>
<div class="card">
<div class="term">
<span class="com">// JetBrains Mono · 13.5 / 1.6</span>
<span class="kw">const</span> <span class="var">aurora</span> = <span class="fn">render</span>(<span class="str">'australis'</span>, { lat: <span class="num">-77.85</span> });
<span class="kw">if</span> (<span class="var">aurora</span>.<span class="fn">visible</span>()) <span class="var">screen</span>.<span class="fn">paint</span>(<span class="tag">#6388d8</span>);
<span class="prompt">$</span> <span class="fn">australis</span> --watch <span class="com"># reload on change</span>
</div>
<div class="meta"><span>--font-mono · JetBrains Mono</span><span>kw · fn · str · num · com</span></div>
</div>
</body></html>
@@ -0,0 +1,27 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>Type · Scale</title>
<link rel="stylesheet" href="_card.css">
<style>
table { width:100%; border-collapse:collapse; }
td { padding: 6px 0; vertical-align: baseline; }
td.token { font-family: var(--font-mono); font-size:11px; color:var(--fg-3); width:80px; letter-spacing:0.04em; }
td.size { font-family: var(--font-mono); font-size:11px; color:var(--fg-2); width:48px; text-align:right; }
td.spec { font-family: var(--font-sans); color: var(--fg-0); font-weight:500; line-height:1.05; padding-left:16px; }
</style>
</head><body>
<div class="card">
<table>
<tr><td class="token">--fs-6xl</td><td class="size">64</td><td class="spec" style="font-size:36px;">Aa Bb 64</td></tr>
<tr><td class="token">--fs-5xl</td><td class="size">48</td><td class="spec" style="font-size:30px;">Aa Bb 48</td></tr>
<tr><td class="token">--fs-4xl</td><td class="size">38</td><td class="spec" style="font-size:26px;">Aa Bb 38</td></tr>
<tr><td class="token">--fs-3xl</td><td class="size">30</td><td class="spec" style="font-size:22px;">Aa Bb 30</td></tr>
<tr><td class="token">--fs-2xl</td><td class="size">24</td><td class="spec" style="font-size:19px;">Aa Bb 24</td></tr>
<tr><td class="token">--fs-xl</td><td class="size">20</td><td class="spec" style="font-size:17px;">Aa Bb 20</td></tr>
<tr><td class="token">--fs-lg</td><td class="size">17</td><td class="spec" style="font-size:15px;">Aa Bb 17</td></tr>
<tr><td class="token">--fs-md</td><td class="size">15</td><td class="spec" style="font-size:14px;">Aa Bb 15 (body)</td></tr>
<tr><td class="token">--fs-sm</td><td class="size">14</td><td class="spec" style="font-size:13px;">Aa Bb 14</td></tr>
<tr><td class="token">--fs-xs</td><td class="size">13</td><td class="spec" style="font-size:12px;">Aa Bb 13</td></tr>
<tr><td class="token">--fs-2xs</td><td class="size">12</td><td class="spec" style="font-size:11px;">Aa Bb 12</td></tr>
</table>
</div>
</body></html>
@@ -0,0 +1,132 @@
// CommandPalette — ⌘K overlay. Glass surface, blue focus ring.
const CommandPalette = ({ open, onClose, onRun }) => {
const [q, setQ] = React.useState('');
const [sel, setSel] = React.useState(0);
const inputRef = React.useRef(null);
const items = React.useMemo(() => [
{ id: 'build', label: 'Build project', hint: '⌘B', kind: 'Action' },
{ id: 'deploy', label: 'Deploy to prod', hint: '⌘D', kind: 'Action' },
{ id: 'theme', label: 'Theme: Australis Dark', hint: '⌘T', kind: 'Setting' },
{ id: 'tokens', label: 'Open tokens.css', hint: 'go', kind: 'File' },
{ id: 'aurora', label: 'Open aurora.ts', hint: 'go', kind: 'File' },
{ id: 'sign', label: 'Sign out', hint: '', kind: 'Action', danger: true },
], []);
const filtered = items.filter(i => i.label.toLowerCase().includes(q.toLowerCase()));
React.useEffect(() => {
if (open && inputRef.current) inputRef.current.focus();
setQ(''); setSel(0);
}, [open]);
React.useEffect(() => {
if (!open) return;
const onKey = (e) => {
if (e.key === 'Escape') onClose();
if (e.key === 'ArrowDown') { e.preventDefault(); setSel(s => Math.min(s + 1, filtered.length - 1)); }
if (e.key === 'ArrowUp') { e.preventDefault(); setSel(s => Math.max(s - 1, 0)); }
if (e.key === 'Enter' && filtered[sel]) { onRun(filtered[sel]); onClose(); }
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [open, sel, filtered, onClose, onRun]);
if (!open) return null;
return (
<div style={cpStyles.scrim} onClick={onClose}>
<div style={cpStyles.panel} onClick={e => e.stopPropagation()}>
<div style={cpStyles.head}>
<Icon name="search" size={16} style={{ color: 'var(--fg-3)' }}/>
<input
ref={inputRef}
value={q}
onChange={e => { setQ(e.target.value); setSel(0); }}
placeholder="Type a command…"
style={cpStyles.input}
/>
<span style={cpStyles.esc}>esc</span>
</div>
<div style={cpStyles.list}>
{filtered.length === 0 && (
<div style={cpStyles.empty}>No matches.</div>
)}
{filtered.map((it, i) => (
<div key={it.id}
onMouseEnter={() => setSel(i)}
onClick={() => { onRun(it); onClose(); }}
style={{
...cpStyles.item,
background: i === sel ? 'rgba(99,136,216,0.18)' : 'transparent',
color: i === sel ? '#a4c4ff' : (it.danger ? '#ff854f' : 'var(--fg-1)'),
}}>
<Icon name={it.kind === 'File' ? 'file' : it.kind === 'Setting' ? 'settings' : 'play'} size={14}
style={{ color: i === sel ? '#a4c4ff' : 'var(--fg-3)' }}/>
<span style={{ flex: 1 }}>{it.label}</span>
<span style={cpStyles.kind}>{it.kind}</span>
{it.hint && <span style={cpStyles.hint}>{it.hint}</span>}
</div>
))}
</div>
</div>
</div>
);
};
const cpStyles = {
scrim: {
position: 'absolute', inset: 0,
background: 'rgba(34,37,49,0.72)',
backdropFilter: 'blur(4px)',
display: 'flex', alignItems: 'flex-start', justifyContent: 'center',
paddingTop: '15vh',
zIndex: 100,
},
panel: {
width: 540,
background: 'rgba(55,59,70,0.85)',
backdropFilter: 'blur(16px)',
border: '1px solid #565f69',
borderRadius: 10,
boxShadow: '0 24px 64px rgba(10,12,18,0.6), 0 8px 16px rgba(10,12,18,0.45)',
overflow: 'hidden',
},
head: {
display: 'flex', alignItems: 'center', gap: 10,
padding: '12px 14px',
borderBottom: '1px solid #414751',
},
input: {
flex: 1, background: 'transparent', border: 0, outline: 'none',
color: 'var(--fg-0)',
fontFamily: 'var(--font-sans)', fontSize: 14,
},
esc: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)', background: '#1b1e28',
padding: '2px 6px', borderRadius: 4, border: '1px solid #414751',
},
list: { padding: 6, maxHeight: 360, overflowY: 'auto' },
item: {
display: 'flex', alignItems: 'center', gap: 10,
padding: '8px 10px',
borderRadius: 5,
fontSize: 13,
cursor: 'pointer',
},
kind: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)',
textTransform: 'uppercase', letterSpacing: '0.06em',
},
hint: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)', background: '#1b1e28',
padding: '2px 6px', borderRadius: 4, border: '1px solid #414751',
},
empty: { padding: 16, fontSize: 13, color: 'var(--fg-3)' },
};
window.CommandPalette = CommandPalette;
@@ -0,0 +1,85 @@
// Editor — tabs + gutter + syntax-highlighted code.
const Editor = ({ openFiles, activeFile, onCloseFile, onSelectFile, fileContent }) => {
return (
<div style={edStyles.root}>
<div style={edStyles.tabs}>
{openFiles.map(f => {
const active = f === activeFile;
return (
<div key={f}
style={{ ...edStyles.tab, ...(active ? edStyles.tabActive : {}) }}
onClick={() => onSelectFile(f)}
>
<Icon name="file" size={12}
style={{ color: active ? '#42dcd1' : 'var(--fg-3)' }}/>
<span>{f.split('/').pop()}</span>
<span
style={edStyles.close}
onClick={(e) => { e.stopPropagation(); onCloseFile(f); }}
>×</span>
</div>
);
})}
<div style={edStyles.tabSpacer}/>
</div>
<div style={edStyles.body}>
<div style={edStyles.gutter}>
{(fileContent[activeFile] || []).map((_, i) => (
<div key={i}>{i + 1}</div>
))}
</div>
<div style={edStyles.code}>
{(fileContent[activeFile] || []).map((line, i) => (
<div key={i} style={edStyles.line}
dangerouslySetInnerHTML={{ __html: line || '&nbsp;' }}/>
))}
</div>
</div>
</div>
);
};
const edStyles = {
root: { flex: 1, display: 'flex', flexDirection: 'column',
background: '#1b1e28', minWidth: 0 },
tabs: {
display: 'flex', background: '#222531',
borderBottom: '1px solid #373b46', overflow: 'hidden',
},
tab: {
display: 'flex', alignItems: 'center', gap: 8,
padding: '8px 14px',
fontFamily: 'var(--font-mono)', fontSize: 12,
color: 'var(--fg-3)',
borderRight: '1px solid #373b46',
cursor: 'pointer',
},
tabActive: {
background: '#1b1e28',
color: 'var(--fg-0)',
boxShadow: 'inset 0 2px 0 #6388d8',
},
tabSpacer: { flex: 1, borderBottom: '1px solid #373b46' },
close: {
fontSize: 14, color: 'var(--fg-3)',
width: 14, height: 14, lineHeight: '12px', textAlign: 'center',
borderRadius: 3,
},
body: { flex: 1, display: 'flex', overflow: 'auto',
fontFamily: 'var(--font-mono)', fontSize: 13.5, lineHeight: 1.7 },
gutter: {
padding: '14px 12px 14px 18px',
color: '#414751',
textAlign: 'right',
userSelect: 'none',
background: '#1b1e28',
borderRight: '1px solid #2a2e38',
fontSize: 12.5,
},
code: { flex: 1, padding: '14px 20px', whiteSpace: 'pre' },
line: {},
};
window.Editor = Editor;
@@ -0,0 +1,45 @@
// Inline Lucide-style icons. 24×24 viewBox, 1.75 stroke, currentColor, no fill.
// Keep the set small — add icons as the kit grows.
const Icon = ({ name, size = 18, style }) => {
const props = {
width: size, height: size, viewBox: "0 0 24 24",
fill: "none", stroke: "currentColor",
strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round",
style,
};
switch (name) {
case "search": return <svg {...props}><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5"/></svg>;
case "chevron-r": return <svg {...props}><path d="M9 6l6 6-6 6"/></svg>;
case "chevron-d": return <svg {...props}><path d="M6 9l6 6 6-6"/></svg>;
case "file": return <svg {...props}><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>;
case "folder": return <svg {...props}><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>;
case "terminal": return <svg {...props}><path d="M4 17l6-6-6-6M12 19h8"/></svg>;
case "settings": return <svg {...props}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1A2 2 0 1 1 4.3 17l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.3-1.8l-.1-.1A2 2 0 1 1 7 4.3l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>;
case "git-branch": return <svg {...props}><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg>;
case "play": return <svg {...props}><polygon points="5 3 19 12 5 21 5 3"/></svg>;
case "x": return <svg {...props}><path d="M18 6L6 18M6 6l12 12"/></svg>;
case "check": return <svg {...props}><path d="M3 12l6 6 12-12"/></svg>;
case "alert": return <svg {...props}><path d="M10.3 3.86a2 2 0 0 1 3.4 0l8.5 14a2 2 0 0 1-1.7 3H3.5a2 2 0 0 1-1.7-3z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>;
case "command": return <svg {...props}><path d="M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3z"/></svg>;
case "circle": return <svg {...props}><circle cx="12" cy="12" r="4" fill="currentColor"/></svg>;
case "plus": return <svg {...props}><path d="M3 12h18M12 3v18"/></svg>;
case "logo": return (
<svg width={size} height={size} viewBox="0 0 64 64" fill="none">
<defs>
<linearGradient id="ig" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#a4c4ff"/>
<stop offset="45%" stopColor="#42dcd1"/>
<stop offset="100%" stopColor="#16b866"/>
</linearGradient>
</defs>
<path d="M10 48 C 14 22, 22 12, 28 8 L 30 8 C 26 18, 22 32, 22 48 Z" fill="url(#ig)" opacity="0.95"/>
<path d="M24 48 C 30 28, 36 18, 42 12 L 44 12 C 38 22, 34 34, 34 48 Z" fill="url(#ig)" opacity="0.7"/>
<path d="M38 48 C 44 32, 50 24, 56 20 L 58 20 C 52 28, 48 38, 48 48 Z" fill="url(#ig)" opacity="0.5"/>
</svg>
);
default: return null;
}
};
window.Icon = Icon;
@@ -0,0 +1,26 @@
# Australis — Terminal UI Kit
The signature surface for Australis: a developer tool combining a terminal, a code editor, a file sidebar, and a status bar. This is what the system was built for.
## Files
- `index.html` — interactive demo wiring everything together
- `Sidebar.jsx` — file tree + workspace switcher
- `Editor.jsx` — tabbed code editor with syntax tokens
- `Terminal.jsx` — bottom panel terminal with prompt + output
- `StatusBar.jsx` — bottom rail (branch, syntax, line/col)
- `CommandPalette.jsx` — ⌘K palette overlay
- `Icon.jsx` — Lucide-style inline icons (1.75 stroke, currentColor)
## What's interactive
- Click sidebar files to switch editor tabs
- Click the workspace pill in the top-left to cycle workspaces
- Type into the terminal prompt and hit `Enter` — outputs are faked but feel right
- Press `⌘K` (or click the search field) to open the command palette
- Hover/focus states honor the system
## Visual sources
- `colors_and_type.css` — all tokens
- `README.md` (root) — visual foundations and content rules
@@ -0,0 +1,131 @@
// Sidebar — workspace switcher + file tree.
const Sidebar = ({ workspace, workspaces, onCycleWorkspace, openFile, activeFile, onOpenFile }) => {
const ws = workspaces[workspace];
return (
<aside style={ssStyles.root}>
<button style={ssStyles.ws} onClick={onCycleWorkspace} title="Switch workspace">
<span style={{ display:'flex', alignItems:'center', gap: 8 }}>
<Icon name="logo" size={20}/>
<span style={ssStyles.wsName}>{ws.name}</span>
</span>
<span style={ssStyles.wsHint}>⌘1</span>
</button>
<div style={ssStyles.search}>
<Icon name="search" size={14} style={{ color: 'var(--fg-3)' }}/>
<input placeholder="Search files…" style={ssStyles.searchInput}/>
<span style={ssStyles.kbd}>⌘K</span>
</div>
<div style={ssStyles.head}>EXPLORER</div>
<div style={ssStyles.tree}>
{ws.tree.map(node => (
<TreeNode key={node.path} node={node} depth={0}
activeFile={activeFile} onOpenFile={onOpenFile}/>
))}
</div>
<div style={ssStyles.foot}>
<Icon name="settings" size={14}/>
<span>settings</span>
</div>
</aside>
);
};
const TreeNode = ({ node, depth, activeFile, onOpenFile }) => {
const [open, setOpen] = React.useState(node.open !== false);
const isFile = !node.children;
const active = activeFile === node.path;
return (
<>
<div
style={{
...ssStyles.row,
paddingLeft: 8 + depth * 14,
background: active ? 'rgba(99,136,216,0.18)' : undefined,
color: active ? '#a4c4ff' : 'var(--fg-1)',
}}
onClick={() => isFile ? onOpenFile(node.path) : setOpen(o => !o)}
>
{!isFile && <Icon name={open ? "chevron-d" : "chevron-r"} size={12} style={{ color:'var(--fg-3)' }}/>}
{isFile && <span style={{ width: 12 }}/>}
<Icon name={isFile ? "file" : "folder"} size={14}
style={{ color: isFile ? 'var(--fg-2)' : '#42dcd1' }}/>
<span style={ssStyles.name}>{node.name}</span>
{node.modified && <span style={ssStyles.modified}>●</span>}
</div>
{!isFile && open && node.children.map(c => (
<TreeNode key={c.path} node={c} depth={depth+1}
activeFile={activeFile} onOpenFile={onOpenFile}/>
))}
</>
);
};
const ssStyles = {
root: {
width: 260, flex: '0 0 260px',
background: '#1f222d',
borderRight: '1px solid #373b46',
display: 'flex', flexDirection: 'column',
fontFamily: 'var(--font-sans)',
color: 'var(--fg-1)',
},
ws: {
display: 'flex', alignItems:'center', justifyContent:'space-between',
padding: '12px 14px',
background: 'transparent', border: 0, borderBottom: '1px solid #373b46',
color: 'var(--fg-0)',
cursor: 'pointer', textAlign: 'left',
fontFamily: 'var(--font-sans)',
},
wsName: { fontSize: 14, fontWeight: 600, letterSpacing: '-0.005em' },
wsHint: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)', background: '#1b1e28',
padding: '2px 6px', borderRadius: 4, border: '1px solid #414751',
},
search: {
display: 'flex', alignItems:'center', gap: 8,
margin: '10px 12px',
padding: '6px 10px',
background: '#1b1e28',
border: '1px solid #414751',
borderRadius: 6,
},
searchInput: {
flex: 1, background: 'transparent', border: 0, outline: 'none',
color: 'var(--fg-1)', fontFamily: 'var(--font-sans)', fontSize: 12,
},
kbd: {
fontFamily: 'var(--font-mono)', fontSize: 10,
color: 'var(--fg-3)',
},
head: {
fontFamily: 'var(--font-mono)', fontSize: 10,
textTransform: 'uppercase', letterSpacing: '0.08em',
color: 'var(--fg-3)',
padding: '8px 14px 4px',
},
tree: { flex: 1, overflowY: 'auto', paddingBottom: 8 },
row: {
display: 'flex', alignItems: 'center', gap: 6,
height: 26, padding: '0 10px 0 8px',
fontSize: 13,
cursor: 'pointer',
userSelect: 'none',
},
name: { flex: 1, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' },
modified: { color:'#42dcd1', fontSize: 12 },
foot: {
display:'flex', alignItems:'center', gap:8,
padding: '10px 14px',
borderTop: '1px solid #373b46',
fontSize: 12, color: 'var(--fg-2)',
},
};
window.Sidebar = Sidebar;
@@ -0,0 +1,44 @@
// StatusBar — bottom rail. Mono, dense, 24px tall.
const StatusBar = ({ branch, problems, position, syntax }) => (
<div style={sbStyles.root}>
<div style={sbStyles.left}>
<span style={sbStyles.cell}>
<Icon name="git-branch" size={12}/> {branch}
</span>
<span style={{ ...sbStyles.cell, color: problems.errors ? '#ff854f' : 'var(--fg-2)' }}>
<Icon name="x" size={12}/> {problems.errors}
</span>
<span style={{ ...sbStyles.cell, color: problems.warnings ? '#ffe14e' : 'var(--fg-2)' }}>
<Icon name="alert" size={12}/> {problems.warnings}
</span>
</div>
<div style={sbStyles.right}>
<span style={sbStyles.cell}>{syntax}</span>
<span style={sbStyles.cell}>UTF-8</span>
<span style={sbStyles.cell}>LF</span>
<span style={sbStyles.cell}>{position}</span>
<span style={{ ...sbStyles.cell, color: '#51e08a' }}>
<Icon name="circle" size={8}/> live
</span>
</div>
</div>
);
const sbStyles = {
root: {
height: 26, flex: '0 0 26px',
display: 'flex', justifyContent: 'space-between',
padding: '0 14px',
background: '#222531',
borderTop: '1px solid #373b46',
fontFamily: 'var(--font-mono)', fontSize: 11,
color: 'var(--fg-2)',
letterSpacing: '0.03em',
},
left: { display:'flex', alignItems:'center', gap: 14 },
right: { display:'flex', alignItems:'center', gap: 14 },
cell: { display:'flex', alignItems:'center', gap: 5 },
};
window.StatusBar = StatusBar;
@@ -0,0 +1,88 @@
// Terminal — bottom panel, prompt + scrollback.
const Terminal = ({ lines, onCommand }) => {
const [input, setInput] = React.useState('');
const scrollRef = React.useRef(null);
React.useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [lines]);
const submit = (e) => {
e.preventDefault();
if (!input.trim()) return;
onCommand(input);
setInput('');
};
return (
<div style={tmStyles.root}>
<div style={tmStyles.head}>
<Icon name="terminal" size={13} style={{ color:'#42dcd1' }}/>
<span style={tmStyles.title}>terminal · zsh</span>
<span style={tmStyles.spacer}/>
<span style={tmStyles.host}>aurora ~/projects/australis</span>
</div>
<div ref={scrollRef} style={tmStyles.body}>
{lines.map((ln, i) => (
<div key={i} style={tmStyles.line}
dangerouslySetInnerHTML={{ __html: ln }}/>
))}
<form onSubmit={submit} style={tmStyles.promptRow}>
<span style={tmStyles.prompt}>➜</span>
<input
value={input}
onChange={e => setInput(e.target.value)}
placeholder="try: build, deploy, theme, help"
style={tmStyles.input}
autoFocus
/>
</form>
</div>
</div>
);
};
const tmStyles = {
root: {
height: 220, flex: '0 0 220px',
background: '#1b1e28',
borderTop: '1px solid #373b46',
display: 'flex', flexDirection: 'column',
},
head: {
display: 'flex', alignItems: 'center', gap: 8,
padding: '6px 14px',
background: '#222531',
borderBottom: '1px solid #373b46',
fontFamily: 'var(--font-mono)', fontSize: 11,
color: 'var(--fg-3)',
letterSpacing: '0.04em',
},
title: { color: 'var(--fg-2)' },
spacer: { flex: 1 },
host: { color: 'var(--fg-3)' },
body: {
flex: 1, overflowY: 'auto',
padding: '10px 18px 14px',
fontFamily: 'var(--font-mono)', fontSize: 13,
lineHeight: 1.65,
color: 'var(--fg-1)',
},
line: { whiteSpace: 'pre-wrap' },
promptRow: {
display: 'flex', alignItems: 'center', gap: 8,
marginTop: 2,
},
prompt: { color: '#42dcd1' },
input: {
flex: 1, background: 'transparent', border: 0, outline: 'none',
fontFamily: 'var(--font-mono)', fontSize: 13,
color: 'var(--fg-0)',
caretColor: '#42dcd1',
},
};
window.Terminal = Terminal;
@@ -0,0 +1,285 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>Australis — Terminal UI Kit</title>
<link rel="stylesheet" href="../../colors_and_type.css"/>
<style>
html, body { height: 100%; margin: 0; }
body {
background: var(--bg-0);
color: var(--fg-1);
font-family: var(--font-sans);
overflow: hidden;
}
.shell {
height: 100vh;
display: flex; flex-direction: column;
position: relative;
}
.titlebar {
height: 36px; flex: 0 0 36px;
display: flex; align-items: center; gap: 14px;
padding: 0 14px;
background: #1b1e28;
border-bottom: 1px solid #373b46;
}
.dots { display: flex; gap: 7px; }
.dot { width: 11px; height: 11px; border-radius: 50%; }
.titlename {
font-family: var(--font-mono);
font-size: 11px;
color: var(--fg-3);
letter-spacing: 0.05em;
flex: 1; text-align: center;
}
.pal-trigger {
display:flex; align-items:center; gap:6px;
background: #222531; border: 1px solid #414751;
color: var(--fg-2);
font-family: var(--font-sans); font-size: 11px;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
}
.pal-trigger:hover { background:#373b46; color: var(--fg-0); }
.body-row { flex: 1; display: flex; min-height: 0; }
</style>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
<script type="text/babel" src="Icon.jsx"></script>
<script type="text/babel" src="Sidebar.jsx"></script>
<script type="text/babel" src="Editor.jsx"></script>
<script type="text/babel" src="Terminal.jsx"></script>
<script type="text/babel" src="StatusBar.jsx"></script>
<script type="text/babel" src="CommandPalette.jsx"></script>
<script type="text/babel">
const FILE_CONTENT = {
'src/aurora.ts': [
`<span style="color:#6e7882;">// Render the southern sky.</span>`,
`<span style="color:#9d78ff;">import</span> { <span style="color:#6388d8;">paint</span> } <span style="color:#9d78ff;">from</span> <span style="color:#16b866;">'./paint'</span>;`,
`<span style="color:#9d78ff;">import</span> <span style="color:#a9bcc3;">type</span> { <span style="color:#42dcd1;">Sky</span> } <span style="color:#9d78ff;">from</span> <span style="color:#16b866;">'./types'</span>;`,
``,
`<span style="color:#9d78ff;">export const</span> <span style="color:#ffe14e;">LATITUDE</span> = <span style="color:#42dcd1;">-77.85</span>;`,
`<span style="color:#9d78ff;">export const</span> <span style="color:#ffe14e;">CURTAINS</span> = <span style="color:#42dcd1;">3</span>;`,
``,
`<span style="color:#9d78ff;">export function</span> <span style="color:#6388d8;">render</span>(<span style="color:#a9bcc3;">name</span>: <span style="color:#42dcd1;">string</span>): <span style="color:#42dcd1;">Sky</span> {`,
`&nbsp;&nbsp;<span style="color:#9d78ff;">if</span> (!<span style="color:#a9bcc3;">name</span>) <span style="color:#9d78ff;">throw new</span> <span style="color:#6388d8;">Error</span>(<span style="color:#16b866;">'name required'</span>);`,
`&nbsp;&nbsp;<span style="color:#9d78ff;">return</span> <span style="color:#6388d8;">paint</span>(<span style="color:#a9bcc3;">name</span>, <span style="color:#ffe14e;">LATITUDE</span>, <span style="color:#ffe14e;">CURTAINS</span>);`,
`}`,
``,
`<span style="color:#6e7882;">// Watch the sky every 800ms.</span>`,
`<span style="color:#6388d8;">setInterval</span>(() <span style="color:#9d78ff;">=&gt;</span> <span style="color:#6388d8;">render</span>(<span style="color:#16b866;">'australis'</span>), <span style="color:#42dcd1;">800</span>);`,
],
'src/tokens.css': [
`<span style="color:#6e7882;">/* Australis tokens — abridged */</span>`,
`<span style="color:#00b1a8;">:root</span> {`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--bg-0</span>: <span style="color:#42dcd1;">#222531</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--bg-1</span>: <span style="color:#42dcd1;">#373b46</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--fg-0</span>: <span style="color:#42dcd1;">#cce7ec</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--fg-1</span>: <span style="color:#42dcd1;">#a9bcc3</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--aus-blue</span>: <span style="color:#42dcd1;">#6388d8</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--aus-cyan</span>: <span style="color:#42dcd1;">#00b1a8</span>;`,
`&nbsp;&nbsp;<span style="color:#a9bcc3;">--aus-green</span>: <span style="color:#42dcd1;">#16b866</span>;`,
`}`,
],
'README.md': [
`<span style="color:#9d78ff;"># australis</span>`,
``,
`<span style="color:#a9bcc3;">A cool, calm dark theme inspired by the southern lights.</span>`,
``,
`<span style="color:#9d78ff;">## install</span>`,
``,
`<span style="color:#16b866;"> npm i @australis/core</span>`,
``,
`<span style="color:#9d78ff;">## use</span>`,
``,
`<span style="color:#a9bcc3;">Import the stylesheet and you're done.</span>`,
],
};
const WORKSPACES = {
prod: {
name: 'Aurora prod',
tree: [
{ name: 'src', path: 'src', open: true, children: [
{ name: 'aurora.ts', path: 'src/aurora.ts', modified: true },
{ name: 'paint.ts', path: 'src/paint.ts' },
{ name: 'tokens.css', path: 'src/tokens.css' },
]},
{ name: 'public', path: 'public', children: [
{ name: 'logo.svg', path: 'public/logo.svg' },
]},
{ name: 'README.md', path: 'README.md' },
{ name: 'package.json', path: 'package.json' },
],
},
staging: {
name: 'Staging',
tree: [
{ name: 'src', path: 'src', open: true, children: [
{ name: 'aurora.ts', path: 'src/aurora.ts' },
{ name: 'tokens.css', path: 'src/tokens.css' },
]},
{ name: 'README.md', path: 'README.md' },
],
},
preview: {
name: 'Preview',
tree: [
{ name: 'src', path: 'src', children: [
{ name: 'aurora.ts', path: 'src/aurora.ts' },
]},
],
},
};
const WS_KEYS = Object.keys(WORKSPACES);
const INITIAL_TERM = [
`<span style="color:#42dcd1;">➜</span> <span style="color:#cce7ec;">australis</span> <span style="color:#9d78ff;">build</span> <span style="color:#a4c4ff;">--watch</span>`,
`<span style="color:#6e7882;"># compiling 24 modules…</span>`,
`<span style="color:#51e08a;">✓</span> tokens compiled <span style="color:#6e7882;">(184ms)</span>`,
`<span style="color:#51e08a;">✓</span> ui-kit bundled <span style="color:#6e7882;">(412ms)</span>`,
`<span style="color:#ffe14e;">!</span> 2 deprecated tokens — see migration.md`,
``,
];
function App() {
const [workspaceIdx, setWorkspaceIdx] = React.useState(0);
const workspace = WS_KEYS[workspaceIdx];
const [openFiles, setOpenFiles] = React.useState(['src/aurora.ts', 'src/tokens.css']);
const [activeFile, setActiveFile] = React.useState('src/aurora.ts');
const [termLines, setTermLines] = React.useState(INITIAL_TERM);
const [paletteOpen, setPaletteOpen] = React.useState(false);
const cycleWorkspace = () => setWorkspaceIdx(i => (i + 1) % WS_KEYS.length);
const openFile = (path) => {
if (!openFiles.includes(path)) setOpenFiles(f => [...f, path]);
setActiveFile(path);
};
const closeFile = (path) => {
const next = openFiles.filter(f => f !== path);
setOpenFiles(next);
if (activeFile === path) setActiveFile(next[0] || null);
};
const runCommand = (cmd) => {
const c = cmd.trim().toLowerCase();
const promptLine = `<span style="color:#42dcd1;">➜</span> <span style="color:#cce7ec;">${cmd}</span>`;
let out = [];
if (c === 'build' || c.startsWith('australis build')) {
out = [
`<span style="color:#6e7882;"># compiling…</span>`,
`<span style="color:#51e08a;">✓</span> built in <span style="color:#42dcd1;">312ms</span>`,
];
} else if (c === 'deploy') {
out = [
`<span style="color:#a4c4ff;">→</span> deploying to ${WORKSPACES[workspace].name.toLowerCase()}…`,
`<span style="color:#51e08a;">✓</span> live at <span style="color:#42dcd1;">https://australis.dev</span>`,
];
} else if (c === 'theme') {
out = [`<span style="color:#a4c4ff;">→</span> theme: <span style="color:#cce7ec;">Australis Dark</span> (active)`];
} else if (c === 'help') {
out = [
`<span style="color:#a9bcc3;">commands:</span>`,
` <span style="color:#9d78ff;">build</span> — compile the project`,
` <span style="color:#9d78ff;">deploy</span> — push to current workspace`,
` <span style="color:#9d78ff;">theme</span> — show active theme`,
` <span style="color:#9d78ff;">clear</span> — clear the terminal`,
];
} else if (c === 'clear') {
setTermLines([]); return;
} else {
out = [`<span style="color:#ff854f;">✗</span> unknown command: <span style="color:#cce7ec;">${cmd}</span> — try <span style="color:#9d78ff;">help</span>`];
}
setTermLines(prev => [...prev, promptLine, ...out, '']);
};
React.useEffect(() => {
const onKey = (e) => {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
e.preventDefault();
setPaletteOpen(true);
}
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, []);
const handlePaletteRun = (item) => {
if (item.id === 'tokens') openFile('src/tokens.css');
else if (item.id === 'aurora') openFile('src/aurora.ts');
else runCommand(item.id);
};
const errors = activeFile === 'src/aurora.ts' ? 0 : 0;
const warnings = 2;
return (
<div className="shell">
<div className="titlebar">
<div className="dots">
<div className="dot" style={{ background:'#ff491a' }}/>
<div className="dot" style={{ background:'#e1c631' }}/>
<div className="dot" style={{ background:'#16b866' }}/>
</div>
<button className="pal-trigger" onClick={() => setPaletteOpen(true)}>
<Icon name="command" size={11}/> command palette
</button>
<div className="titlename">australis · {WORKSPACES[workspace].name.toLowerCase()}</div>
<div style={{ width: 80 }}/>
</div>
<div className="body-row">
<Sidebar
workspace={workspace}
workspaces={WORKSPACES}
onCycleWorkspace={cycleWorkspace}
openFile={openFile}
activeFile={activeFile}
onOpenFile={openFile}
/>
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
<Editor
openFiles={openFiles}
activeFile={activeFile}
onCloseFile={closeFile}
onSelectFile={setActiveFile}
fileContent={FILE_CONTENT}
/>
<Terminal lines={termLines} onCommand={runCommand}/>
</div>
</div>
<StatusBar
branch="main"
problems={{ errors, warnings }}
position="ln 8 · col 24"
syntax={activeFile && activeFile.endsWith('.ts') ? 'TypeScript'
: activeFile && activeFile.endsWith('.css') ? 'CSS'
: 'Markdown'}
/>
<CommandPalette
open={paletteOpen}
onClose={() => setPaletteOpen(false)}
onRun={handlePaletteRun}
/>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
</body>
</html>
@@ -0,0 +1,89 @@
<img src="https://github.com/user-attachments/assets/6aef8556-07d0-4426-bcbe-509734df83ed" alt="AustralisLogo" width="200" />
# Australis Dark Color Theme
The Australis Dark color theme is inspired by the Southern Lights: The Aurora Australis
The theme consists of 16 terminal colors and a specification for usage. It is designed to be medium contrast and easy on the eyes as well as having an aesthetic appeal.
It is based on the following design criteria:
- All colors are cooler than neutral.
- With the exception of black, white and yellow, ANSI terminal colors have a perceived lab lightness of 65. _Bright_ variants have a lab lightness of 80.
- As neutral colors get brighter, they shift slightly towards green while maintaining the overall cool blue temperature. This is a design decision to give it an antactic ocean aurora theme.
- Overall contrast is medium to medium high to maintain eye comfort.
## Ice - Main Colors
These colors are the base colors. The background is a slightly cool light black without becoming too blue. As the primary colors increase in brightness, they start tinting more green. These are the primary colors that should make up the background and foreground.
| ![#222531](https://placehold.co/100/222531/222531) | ![#a9bcc3](https://placehold.co/100/a9bcc3/a9bcc3) | ![#cce7ec](https://placehold.co/100/cce7ec/cce7ec) |
| :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: |
| #222531 | #a9bcc3 | #c7e7f9 |
| Background | Foreground | Highlight |
| Black | White | Bright White |
## Aurora - Primary Colors
These make up the bulk of the color volume for the theme. These colors can be used generously. By default, blue, cyan and green are used in that order of preference. Bright variants are used for highlighting purposes.
| ![#6388D8](https://placehold.co/100/6388D8/6388D8) | ![#a4c4ff](https://placehold.co/100/a4c4ff/a4c4ff) | ![#00b1a8](https://placehold.co/100/00b1a8/00b1a8) | ![#42dcd1](https://placehold.co/100/42dcd1/42dcd1) | ![#16B866](https://placehold.co/100/16B866/16B866) | ![#51e08a](https://placehold.co/100/51e08a/51e08a) |
| :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: |
| #6388D8 | #a4c4ff | #00b1a8 | #42dcd1 | #16B866 | #51e08a |
| Blue | Bright Blue | Cyan | Bright Cyan | Green | Bright Green |
## Dawn - Accent Colors
These should be used sparingly for the theme.
| ![#ff491a](https://placehold.co/100/ff491a/ff491a) | ![#ff854f](https://placehold.co/100/ff854f/ff854f) | ![#e1c631](https://placehold.co/100/e1c631/e1c631) | ![#FFE14E](https://placehold.co/100/FFE14E/FFE14E) | ![#9d78ff](https://placehold.co/100/9d78ff/9d78ff) | ![#d8adff](https://placehold.co/100/d8adff/d8adff) |
| :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: |
| #ff491a | #ff854f | #e1c631 | #FFE14E | #9d78ff | #d8adff |
| Red | Bright Red | Yellow | Bright Yellow | Magenta | Bright Magenta |
## Sea - Neutral Contrast Colors
These are neutral tones between black and bright white for adjusting contrast and for the representation of text on the colors and themes with varying degrees of contrast. Black, Bright Black, White and Bright White can be substituted for any of the contrast colors to give the desired contrast. Uses for the darks include low-contrast elements like comments. As with all neutral tones in the theme, green tint is progressively increased with brightness.
### Darks
| ![#222531](https://placehold.co/100/222531/222531) | ![#373b46](https://placehold.co/100/373b46/373b46) | ![#414751](https://placehold.co/100/414751/414751) | ![#565f69](https://placehold.co/100/565f69/565f69) | ![#6e7882](https://placehold.co/100/6e7882/6e7882) | ![#86929d](https://placehold.co/100/86929d/86929d) |
| :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: |
| #222531 | #373b46 | #414751 | #565f69 | #6e7882 | #86929d |
| Black (LAB 15) | Bright Black (LAB 25) | Dark_30 (LAB 30) | Dark_40 (LAB 40) | Dark_50 (Lab 50) | Dark_60 (Lab 60) |
### Brights
| ![#9daeb6](https://placehold.co/100/9daeb6/9daeb6) | ![#a9bcc3](https://placehold.co/100/a9bcc3/a9bcc3) | ![#b3cbcf](https://placehold.co/100/b3cbcf/b3cbcf) | ![#cce7ec](https://placehold.co/100/cce7ec/cce7ec) |
| :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: | :------------------------------------------------: |
| #9daeb6 | #a9bcc3 | #b3cbcf | #cce7ec |
| Bright 70 (LAB 70) | White (Lab 75) | Bright_80 (LAB 80) | Bright White (LAB 90) |
## All Colors and Hex Codes
| Swatch | Hex | Color |
| -------------------------------------------------- | :-----: | :------------- |
| ![#222531](https://placehold.co/100/222531/222531) | #222531 | black |
| ![#373b46](https://placehold.co/100/373b46/373b46) | #373b46 | bright black |
| ![#a9bcc3](https://placehold.co/100/a9bcc3/a9bcc3) | #a9bcc3 | white |
| ![#cce7ec](https://placehold.co/100/cce7ec/cce7ec) | #cce7ec | bright white |
| ![#ff491a](https://placehold.co/100/ff491a/ff491a) | #ff491a | red |
| ![#ff854f](https://placehold.co/100/ff854f/ff854f) | #ff854f | bright red |
| ![#16B866](https://placehold.co/100/16B866/16B866) | #16B866 | green |
| ![#51e08a](https://placehold.co/100/51e08a/51e08a) | #51e08a | bright green |
| ![#6388D8](https://placehold.co/100/6388D8/6388D8) | #6388D8 | blue |
| ![#a4c4ff](https://placehold.co/100/a4c4ff/a4c4ff) | #a4c4ff | bright blue |
| ![#e1c631](https://placehold.co/100/e1c631/e1c631) | #e1c631 | yellow |
| ![#FFE14E](https://placehold.co/100/FFE14E/FFE14E) | #FFE14E | bright yellow |
| ![#9d78ff](https://placehold.co/100/9d78ff/9d78ff) | #9d78ff | magenta |
| ![#d8adff](https://placehold.co/100/d8adff/d8adff) | #d8adff | bright magenta |
| ![#00b1a8](https://placehold.co/100/00b1a8/00b1a8) | #00b1a8 | cyan |
| ![#42dcd1](https://placehold.co/100/42dcd1/42dcd1) | #42dcd1 | bright cyan |
| ![#414751](https://placehold.co/100/414751/414751) | #414751 | dark 30 |
| ![#565f69](https://placehold.co/100/565f69/565f69) | #565f69 | dark 40 |
| ![#6e7882](https://placehold.co/100/6e7882/6e7882) | #6e7882 | dark 50 |
| ![#86929d](https://placehold.co/100/86929d/86929d) | #86929d | dark 60 |
| ![#9daeb6](https://placehold.co/100/9daeb6/9daeb6) | #9daeb6 | bright 70 |
| ![#b3cbcf](https://placehold.co/100/b3cbcf/b3cbcf) | #b3cbcf | bright 80 |
Copyright 2024 Vuong Hoang
+5
View File
@@ -0,0 +1,5 @@
[user]
name = Vuong Hoang
email = vh@phasefinal.com
[push]
followTags = true
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Seed a second Claude Code config dir that shares everything login-independent
# with ~/.claude (symlinks) but holds its own credentials and its own
# .claude.json (copied once, so MCP servers and onboarding carry over; /login
# then rebinds the account). Usage: claude-config-dir-init /abs/path/.claude-<name>
set -euo pipefail
target="${1:?usage: claude-config-dir-init /abs/path}"
case "$target" in /*) ;; *) echo "use an absolute path (no ~ inside quotes)" >&2; exit 2;; esac
src="$HOME/.claude"
mkdir -p "$target"
shared=(CLAUDE.md CONTEXT.md skills plugins hooks agents commands settings.json settings.local.json keybindings.json statusline-command.sh bin projects projects-archive plans feedback)
for item in "${shared[@]}"; do
[ -e "$src/$item" ] || continue
if [ -e "$target/$item" ] && [ ! -L "$target/$item" ]; then echo "skip $item: real file already in $target" >&2; continue; fi
ln -sfn "$src/$item" "$target/$item"
done
if [ ! -e "$target/.claude.json" ] && [ -f "$HOME/.claude.json" ]; then cp "$HOME/.claude.json" "$target/.claude.json"; chmod 600 "$target/.claude.json"; fi
echo "seeded $target"; ls -la "$target" | sed -n '2,40p'
echo
echo "next: CLAUDE_CONFIG_DIR=\"$target\" claude → /login once; credentials land in $target/.credentials.json"
+28
View File
@@ -0,0 +1,28 @@
[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env"
# user-level secrets (HF_TOKEN etc.)
[ -f "$HOME/.config/secrets/env.sh" ] && source "$HOME/.config/secrets/env.sh"
# fleet CLIs (booth, secret symlinks) reachable from non-interactive ssh too
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
# safe-rm (installed 2026-09-22, operator's call) — a wrapper that REFUSES to
# delete the ~25 protected system paths in /etc/safe-rm.conf: /, /bin, /etc,
# /home, /usr, /var and friends. Guards the rm -rf /usr class of mistake.
#
# ⚠ IT IS SET HERE AND NOT LEFT TO THE PACKAGE. safe-rm ships
# /etc/profile.d/safe-rm.sh, which /etc/profile walks correctly -- but Debian's
# /etc/zsh/zprofile is entirely commented out (0 non-comment lines), so a zsh
# LOGIN shell never sources /etc/profile and the hook never fires. Measured
# immediately after install: rm still resolved to /usr/bin/rm in this shell, in
# a fresh `zsh -l`, AND over ssh. Installed and inert.
#
# .zshenv is the right home because it is sourced for EVERY zsh invocation,
# including the non-interactive `ssh host '<cmd>'` form that is how most fleet
# work actually runs -- the same reason ~/.local/bin is set here.
#
# ⚠ AND IT DOES NOT COVER THE MISTAKE THAT PROMPTED IT. safe-rm is a path
# blacklist, not a heuristic. Measured: it refuses `rm -rf /home`, and it
# deletes a file whose path came from an unset variable without complaint. The
# variable-path habit is still guarded only by discipline and `set -u`.
[ -d /usr/share/safe-rm/bin ] && export PATH="/usr/share/safe-rm/bin:$PATH"
+30
View File
@@ -10,11 +10,41 @@
#
# Windows / PowerShell: run ./link-dotfiles.ps1 instead (stow has no native
# Windows port; the .ps1 mirrors this with junctions + symlinks).
#
# `--check`: report drift and change nothing. Two kinds are caught. (1) A tracked
# file whose live copy is no longer a link into this repo: stow would refuse to
# stow over it, so the repo copy has silently gone stale. `sed -i` and any other
# write-temp-then-rename editor does this, and so can an app that rewrites its
# own config atomically. (2) A link into this repo whose target has been removed.
# Exits 1 on drift.
set -euo pipefail
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$repo"
if [ "${1:-}" = "--check" ]; then
command -v stow >/dev/null 2>&1 || { echo "error: GNU stow not installed; run ./link-dotfiles once first." >&2; exit 2; }
drift=0
for spec in "home_root:$HOME" "home_config:$HOME/.config"; do
pkg="${spec%%:*}"; target="${spec#*:}"
out="$(stow -n -v --target="$target" "$pkg" 2>&1 | grep -v 'simulation mode' || true)"
if [ -n "$out" ]; then
drift=1
echo "✗ $pkg -> $target is not fully linked:"
printf '%s\n' "$out" | sed 's/^/ /'
fi
done
dangling="$(find "$HOME" "$HOME/.config" "$HOME/.claude" "$HOME/.claude/bin" "$HOME/.claude/skills" "$HOME/.local/bin" \
-maxdepth 1 -xtype l -lname "*dotfiles/*" 2>/dev/null || true)"
if [ -n "$dangling" ]; then
drift=1
echo "✗ links into dotfiles whose target is gone:"
printf '%s\n' "$dangling" | sed 's/^/ /'
fi
[ "$drift" -eq 0 ] && echo "✓ no drift: every tracked file is linked, and no dotfiles link dangles."
exit "$drift"
fi
# `--apps`: install all host CLI tools first, then link. Without it, this only
# links (fast + idempotent). App install is delegated to the host-aware
# lk-installers/install-apps.