
Web Artifacts Builder V2
- 22 installs
- 154 repo stars
- Updated July 30, 2026
- sammcj/agentic-coding
Helps with ai & agent building tasks.
About
web-artifacts-builder-v2 is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- web-artifacts-builder-v2
- AI & Agent Building
- AI-coding skill
Web Artifacts Builder V2 by the numbers
- 22 all-time installs (skills.sh)
- +1 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #10,123 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 1, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sammcj/agentic-coding --skill web-artifacts-builder-v2Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 22 |
|---|---|
| repo stars | ★ 154 |
| Last updated | July 30, 2026 |
| Repository | sammcj/agentic-coding ↗ |
What it does
Helps with ai & agent building tasks.
Files
Web Artefacts Builder
To build a frontend claude.ai artefact, follow these steps: 1. Initialise the frontend repo with scripts/init-artifact.sh 2. Pick a theme from resources/themes.css and apply it (see "Themes" below) 3. Develop your artefact by editing the generated code 4. Bundle to a single HTML file with scripts/bundle-artifact.sh 5. Share the bundled artefact with the user 6. (Optional) Test the artefact
Stack: React 19 + TypeScript + Vite 8 + Tailwind CSS v4 + shadcn/ui (new-york style) + lucide-react. Bundling via vite-plugin-singlefile. Requires Node 20.19+ or 22.12+.
Design principles
These principles are adapted from the html-design-examples skill. Load that skill alongside this one when you need patterns for specific artefact intents (status report, code review, PR write-up, design system page, editor, etc.); its rendered catalogue at resources/index.html indexes 20 single-file references.
- Calm, editorial aesthetic. Restrained accent colour, generous whitespace, content does the talking. Avoid hero gradients, marketing sheen, and dashboard-style chrome unless the artefact is a dashboard.
- Typography over chrome. Establish hierarchy with weight, size, and leading before reaching for borders, backgrounds, or coloured bars. Serif headings paired with sans body is the recurring pattern in the references and a reliable choice when you have no other signal. Defaulting to Inter as the only typeface is an AI-output tell - pick a pairing from the chosen theme's font notes.
- Earn your accent. Most themes ship with a single accent colour. Treat it as scarce. Use it for the one thing on the page that demands attention (the primary action, the link, the focused state) and almost nowhere else.
- Use the full width when it helps. Don't centre a narrow column when the content is tabular, comparative, or spatial. A wide layout with structured columns reads better than a 720px text shaft for anything that isn't longform prose.
- Export interactive state. If the artefact is an editor, board, or any surface where the user is doing work, finish it with a "copy as JSON" or "copy as prompt" button so the result flows back into the agent loop. See the
18-editor-triage-board.htmlexample inhtml-design-examplesfor the pattern.
Anti-slop rule
Use visual accents (coloured border stripes, tinted-background callout boxes, accent bars on cards) only when the colour or treatment encodes specific information - status, category, severity, or priority that I've explicitly defined. Don't apply visual accents decoratively, don't rotate through palette colours across sibling items for variety, and don't reach for the tinted-callout-with-coloured-bar pattern as a default way to mark asides. If you're about to add an accent, ask whether removing it would lose information; if not, remove it and rely on plain typographic hierarchy instead.
Other things to avoid because they read as AI-default output: excessive centred layouts, purple gradients, uniformly rounded corners on everything, glassmorphism, emoji bullets, and three-card-grid hero sections with identical icons.
Themes
resources/themes.css ships five named themes. Pick one before you start writing components - retrofitting a palette is much more work than choosing one up front.
| Theme | Mood | Good for |
|---|---|---|
| Editorial paper | Warm paper, ink, ochre accent | Longform writeups, plans, reports, post-mortems |
| Technical mono | Near-monochrome, single green accent | Code reviews, system diagrams, status pages, debugging |
| Warm desaturated | Sand, clay-brown, muted terracotta | Brand-adjacent artefacts, design pages, anything that wants warmth without volume |
| Cool muted | Quiet slate, low-chroma teal | Dashboards, status reports, technical docs |
| Nordic dark | Polar-night greys, frost-blue | Incident write-ups, debug dumps, dark-mode-first developer tooling |
To apply a theme: 1. Read resources/themes.css and copy the :root { ... } and .dark { ... } blocks for the chosen theme. 2. Replace the existing :root and .dark blocks in your project's src/index.css. Leave the @theme inline and @layer base blocks alone - they wire the variables into Tailwind utilities. 3. Add the suggested font stack (the comment block above each theme lists one) - either via a <link rel="stylesheet"> tag in index.html from Google Fonts, or by setting body { font-family: ... } in src/index.css.
If the user has an existing brand or design system, defer to it rather than picking from the gallery.
Quick start
Step 1: Initialise the project
bash scripts/init-artifact.sh <project-name>
cd <project-name>This creates a configured project with:
- React 19 + TypeScript via Vite 8
- Tailwind CSS v4 (CSS-first config in
src/index.css, notailwind.config.js) - 40+ shadcn/ui components in
src/components/ui/ radix-uiumbrella package and lucide-react- Path alias
@/configured in bothtsconfig.jsonandvite.config.ts - Minimal
App.tsxplaceholder ready for you to edit
Step 2: Apply a theme
See "Themes" above.
Step 3: Develop the artefact
Edit src/App.tsx and add components as needed. Components are imported from @/components/ui/<name>. The cn() helper lives at @/lib/utils.
To preview during development:
pnpm devStep 4: Bundle to a single HTML file
bash scripts/bundle-artifact.shThis produces bundle.html - a single self-contained file with all JS, CSS, and assets inlined. The script:
- Adds
vite-plugin-singlefileas a dev dependency - Writes a
vite.config.bundle.tsthat extends your config with the single-file plugin and inlining options - Runs
vite build --config vite.config.bundle.ts - Renames
dist/index.htmltobundle.html
The artefact requires nothing at runtime - hand it to the user directly.
Step 5: Share with the user
Pass bundle.html back to the user as the artefact.
Step 6: Test (optional)
Only test before sharing if the user has asked, or if the artefact has interactive logic that you don't want to ship broken. Use Playwright or open the file locally. Otherwise prefer shipping fast and iterating on feedback.
Reference
- shadcn/ui components: https://ui.shadcn.com/docs/components
- Tailwind CSS v4 docs: https://tailwindcss.com/docs
- Companion skill for design patterns:
html-design-examples
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2026 Anthropic, PBC.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License./*
* Named themes for web-artifacts-builder
*
* Each theme below is a drop-in replacement for the `:root` and `.dark` blocks
* in `src/index.css`. The `@theme inline` mapping and `@layer base` rules in
* the generated `src/index.css` already wire these variables into Tailwind
* utilities - do not edit those.
*
* Each theme defines all variables used by shadcn/ui components, including
* --chart-1..5 (used by the Chart components) and --sidebar-* (used by the
* Sidebar component). Swap in :root + .dark wholesale; you do not need to
* preserve any of the original values.
*
* To apply a theme:
* 1. Pick one that fits the artefact's mood (see notes under each).
* 2. Replace the `:root { ... }` and `.dark { ... }` blocks in src/index.css
* with the corresponding pair below.
* 3. Apply the suggested font stack (add it to <head> via <link>, or import
* via a Google Fonts URL, then set `body { font-family: ... }` and a
* `:root { --font-heading: ... }` rule as needed).
*
* All colours use OKLCH (perceptually uniform), which is what shadcn/ui
* defaults to under Tailwind v4. The accent value is intentionally the only
* chromatic colour in most themes - see the accent-discipline rule in
* SKILL.md before reaching for it.
*/
/* =========================================================================
* 1. Editorial paper
*
* Warm off-white background, deep ink foreground, a single ochre accent.
* Suits: longform writeups, plans, reports, post-mortems, anything that
* wants to feel like a printed page. Pair serif headings with sans body.
*
* Suggested fonts:
* headings: "Source Serif 4", "Charter", Georgia, serif
* body: "Inter Tight", "IBM Plex Sans", system-ui, sans-serif
* mono: "JetBrains Mono", ui-monospace, monospace
* ========================================================================= */
:root {
--radius: 0.5rem;
--background: oklch(0.975 0.008 85);
--foreground: oklch(0.22 0.018 60);
--card: oklch(0.985 0.006 85);
--card-foreground: oklch(0.22 0.018 60);
--popover: oklch(0.985 0.006 85);
--popover-foreground: oklch(0.22 0.018 60);
--primary: oklch(0.28 0.025 55);
--primary-foreground: oklch(0.975 0.008 85);
--secondary: oklch(0.93 0.012 80);
--secondary-foreground: oklch(0.28 0.025 55);
--muted: oklch(0.93 0.012 80);
--muted-foreground: oklch(0.50 0.020 60);
--accent: oklch(0.55 0.115 50);
--accent-foreground: oklch(0.975 0.008 85);
--destructive: oklch(0.52 0.18 25);
--destructive-foreground: oklch(0.975 0.008 85);
--border: oklch(0.86 0.012 80);
--input: oklch(0.86 0.012 80);
--ring: oklch(0.55 0.115 50);
--chart-1: oklch(0.55 0.115 50);
--chart-2: oklch(0.48 0.090 30);
--chart-3: oklch(0.62 0.075 90);
--chart-4: oklch(0.42 0.080 20);
--chart-5: oklch(0.58 0.060 70);
--sidebar: oklch(0.96 0.010 80);
--sidebar-foreground: oklch(0.22 0.018 60);
--sidebar-primary: oklch(0.28 0.025 55);
--sidebar-primary-foreground: oklch(0.975 0.008 85);
--sidebar-accent: oklch(0.93 0.012 80);
--sidebar-accent-foreground: oklch(0.28 0.025 55);
--sidebar-border: oklch(0.86 0.012 80);
--sidebar-ring: oklch(0.55 0.115 50);
}
.dark {
--background: oklch(0.18 0.015 60);
--foreground: oklch(0.92 0.018 80);
--card: oklch(0.22 0.018 60);
--card-foreground: oklch(0.92 0.018 80);
--popover: oklch(0.22 0.018 60);
--popover-foreground: oklch(0.92 0.018 80);
--primary: oklch(0.92 0.018 80);
--primary-foreground: oklch(0.22 0.018 60);
--secondary: oklch(0.26 0.020 60);
--secondary-foreground: oklch(0.92 0.018 80);
--muted: oklch(0.26 0.020 60);
--muted-foreground: oklch(0.68 0.020 70);
--accent: oklch(0.68 0.095 55);
--accent-foreground: oklch(0.18 0.015 60);
--destructive: oklch(0.62 0.18 25);
--destructive-foreground: oklch(0.18 0.015 60);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 14%);
--ring: oklch(0.68 0.095 55);
--chart-1: oklch(0.68 0.095 55);
--chart-2: oklch(0.62 0.080 30);
--chart-3: oklch(0.72 0.070 90);
--chart-4: oklch(0.55 0.080 20);
--chart-5: oklch(0.65 0.060 70);
--sidebar: oklch(0.20 0.018 60);
--sidebar-foreground: oklch(0.92 0.018 80);
--sidebar-primary: oklch(0.68 0.095 55);
--sidebar-primary-foreground: oklch(0.18 0.015 60);
--sidebar-accent: oklch(0.26 0.020 60);
--sidebar-accent-foreground: oklch(0.92 0.018 80);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.68 0.095 55);
}
/* =========================================================================
* 2. Technical mono
*
* Near-monochrome greyscale with a single green-amber accent reserved for
* status, links, and focused state. Reads like a terminal or a technical
* spec. Suits: code reviews, system diagrams, status pages, debugging
* artefacts. Use a mono or geometric sans throughout.
*
* Suggested fonts:
* headings: "Geist Mono", "JetBrains Mono", ui-monospace, monospace
* body: "Geist", "Inter Tight", system-ui, sans-serif
* mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace
* ========================================================================= */
:root {
--radius: 0.25rem;
--background: oklch(0.99 0 0);
--foreground: oklch(0.18 0 0);
--card: oklch(0.99 0 0);
--card-foreground: oklch(0.18 0 0);
--popover: oklch(0.99 0 0);
--popover-foreground: oklch(0.18 0 0);
--primary: oklch(0.20 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.95 0 0);
--secondary-foreground: oklch(0.20 0 0);
--muted: oklch(0.95 0 0);
--muted-foreground: oklch(0.50 0 0);
--accent: oklch(0.62 0.16 145);
--accent-foreground: oklch(0.16 0 0);
--destructive: oklch(0.55 0.20 25);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.88 0 0);
--input: oklch(0.88 0 0);
--ring: oklch(0.62 0.16 145);
--chart-1: oklch(0.62 0.16 145);
--chart-2: oklch(0.55 0.11 145);
--chart-3: oklch(0.45 0.07 145);
--chart-4: oklch(0.40 0 0);
--chart-5: oklch(0.30 0 0);
--sidebar: oklch(0.97 0 0);
--sidebar-foreground: oklch(0.18 0 0);
--sidebar-primary: oklch(0.20 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.95 0 0);
--sidebar-accent-foreground: oklch(0.20 0 0);
--sidebar-border: oklch(0.88 0 0);
--sidebar-ring: oklch(0.62 0.16 145);
}
.dark {
--background: oklch(0.14 0 0);
--foreground: oklch(0.92 0 0);
--card: oklch(0.18 0 0);
--card-foreground: oklch(0.92 0 0);
--popover: oklch(0.18 0 0);
--popover-foreground: oklch(0.92 0 0);
--primary: oklch(0.92 0 0);
--primary-foreground: oklch(0.18 0 0);
--secondary: oklch(0.22 0 0);
--secondary-foreground: oklch(0.92 0 0);
--muted: oklch(0.22 0 0);
--muted-foreground: oklch(0.68 0 0);
--accent: oklch(0.74 0.16 145);
--accent-foreground: oklch(0.14 0 0);
--destructive: oklch(0.65 0.20 25);
--destructive-foreground: oklch(0.14 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 14%);
--ring: oklch(0.74 0.16 145);
--chart-1: oklch(0.74 0.16 145);
--chart-2: oklch(0.66 0.11 145);
--chart-3: oklch(0.56 0.07 145);
--chart-4: oklch(0.62 0 0);
--chart-5: oklch(0.72 0 0);
--sidebar: oklch(0.16 0 0);
--sidebar-foreground: oklch(0.92 0 0);
--sidebar-primary: oklch(0.92 0 0);
--sidebar-primary-foreground: oklch(0.18 0 0);
--sidebar-accent: oklch(0.22 0 0);
--sidebar-accent-foreground: oklch(0.92 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.74 0.16 145);
}
/* =========================================================================
* 3. Warm desaturated
*
* Sand background, clay-brown foreground, muted terracotta accent. Dialled-
* down warm palette - the saturation never gets loud. Suits: design system
* pages, brand-adjacent artefacts, anything that wants warmth without
* looking like a Mediterranean restaurant menu.
*
* Suggested fonts:
* headings: "Fraunces", "Charter", Georgia, serif
* body: "Inter Tight", "IBM Plex Sans", system-ui, sans-serif
* mono: "IBM Plex Mono", ui-monospace, monospace
* ========================================================================= */
:root {
--radius: 0.5rem;
--background: oklch(0.955 0.013 70);
--foreground: oklch(0.28 0.025 40);
--card: oklch(0.97 0.010 70);
--card-foreground: oklch(0.28 0.025 40);
--popover: oklch(0.97 0.010 70);
--popover-foreground: oklch(0.28 0.025 40);
--primary: oklch(0.36 0.030 35);
--primary-foreground: oklch(0.955 0.013 70);
--secondary: oklch(0.92 0.018 70);
--secondary-foreground: oklch(0.36 0.030 35);
--muted: oklch(0.92 0.018 70);
--muted-foreground: oklch(0.52 0.025 50);
--accent: oklch(0.56 0.095 30);
--accent-foreground: oklch(0.97 0.010 70);
--destructive: oklch(0.52 0.17 25);
--destructive-foreground: oklch(0.97 0.010 70);
--border: oklch(0.85 0.016 65);
--input: oklch(0.85 0.016 65);
--ring: oklch(0.56 0.095 30);
--chart-1: oklch(0.56 0.095 30);
--chart-2: oklch(0.50 0.070 50);
--chart-3: oklch(0.60 0.060 80);
--chart-4: oklch(0.42 0.080 25);
--chart-5: oklch(0.65 0.050 40);
--sidebar: oklch(0.94 0.014 70);
--sidebar-foreground: oklch(0.28 0.025 40);
--sidebar-primary: oklch(0.36 0.030 35);
--sidebar-primary-foreground: oklch(0.955 0.013 70);
--sidebar-accent: oklch(0.92 0.018 70);
--sidebar-accent-foreground: oklch(0.36 0.030 35);
--sidebar-border: oklch(0.85 0.016 65);
--sidebar-ring: oklch(0.56 0.095 30);
}
.dark {
--background: oklch(0.20 0.020 35);
--foreground: oklch(0.90 0.015 70);
--card: oklch(0.24 0.022 35);
--card-foreground: oklch(0.90 0.015 70);
--popover: oklch(0.24 0.022 35);
--popover-foreground: oklch(0.90 0.015 70);
--primary: oklch(0.90 0.015 70);
--primary-foreground: oklch(0.24 0.022 35);
--secondary: oklch(0.28 0.022 35);
--secondary-foreground: oklch(0.90 0.015 70);
--muted: oklch(0.28 0.022 35);
--muted-foreground: oklch(0.66 0.020 60);
--accent: oklch(0.66 0.090 30);
--accent-foreground: oklch(0.20 0.020 35);
--destructive: oklch(0.62 0.17 25);
--destructive-foreground: oklch(0.20 0.020 35);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 14%);
--ring: oklch(0.66 0.090 30);
--chart-1: oklch(0.66 0.090 30);
--chart-2: oklch(0.60 0.070 50);
--chart-3: oklch(0.70 0.060 80);
--chart-4: oklch(0.55 0.080 25);
--chart-5: oklch(0.72 0.050 40);
--sidebar: oklch(0.18 0.018 35);
--sidebar-foreground: oklch(0.90 0.015 70);
--sidebar-primary: oklch(0.90 0.015 70);
--sidebar-primary-foreground: oklch(0.20 0.020 35);
--sidebar-accent: oklch(0.26 0.022 35);
--sidebar-accent-foreground: oklch(0.90 0.015 70);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.66 0.090 30);
}
/* =========================================================================
* 4. Cool muted
*
* Quiet slate background, dark slate foreground, low-chroma blue-teal
* accent. The cool counterpart to "Warm desaturated". Suits: dashboards,
* status reports, technical documentation, anything that should read as
* calm and considered.
*
* Suggested fonts:
* headings: "Inter Tight", "IBM Plex Sans", system-ui, sans-serif
* body: "Inter Tight", "IBM Plex Sans", system-ui, sans-serif
* mono: "JetBrains Mono", ui-monospace, monospace
* ========================================================================= */
:root {
--radius: 0.5rem;
--background: oklch(0.97 0.005 230);
--foreground: oklch(0.25 0.025 240);
--card: oklch(0.98 0.004 230);
--card-foreground: oklch(0.25 0.025 240);
--popover: oklch(0.98 0.004 230);
--popover-foreground: oklch(0.25 0.025 240);
--primary: oklch(0.30 0.030 240);
--primary-foreground: oklch(0.97 0.005 230);
--secondary: oklch(0.93 0.010 230);
--secondary-foreground: oklch(0.30 0.030 240);
--muted: oklch(0.93 0.010 230);
--muted-foreground: oklch(0.50 0.022 230);
--accent: oklch(0.55 0.070 215);
--accent-foreground: oklch(0.97 0.005 230);
--destructive: oklch(0.55 0.18 25);
--destructive-foreground: oklch(0.97 0.005 230);
--border: oklch(0.87 0.008 230);
--input: oklch(0.87 0.008 230);
--ring: oklch(0.55 0.070 215);
--chart-1: oklch(0.55 0.070 215);
--chart-2: oklch(0.50 0.060 200);
--chart-3: oklch(0.60 0.050 250);
--chart-4: oklch(0.45 0.060 180);
--chart-5: oklch(0.65 0.040 220);
--sidebar: oklch(0.96 0.006 230);
--sidebar-foreground: oklch(0.25 0.025 240);
--sidebar-primary: oklch(0.30 0.030 240);
--sidebar-primary-foreground: oklch(0.97 0.005 230);
--sidebar-accent: oklch(0.93 0.010 230);
--sidebar-accent-foreground: oklch(0.30 0.030 240);
--sidebar-border: oklch(0.87 0.008 230);
--sidebar-ring: oklch(0.55 0.070 215);
}
.dark {
--background: oklch(0.17 0.015 240);
--foreground: oklch(0.92 0.010 220);
--card: oklch(0.21 0.017 240);
--card-foreground: oklch(0.92 0.010 220);
--popover: oklch(0.21 0.017 240);
--popover-foreground: oklch(0.92 0.010 220);
--primary: oklch(0.92 0.010 220);
--primary-foreground: oklch(0.21 0.017 240);
--secondary: oklch(0.25 0.018 240);
--secondary-foreground: oklch(0.92 0.010 220);
--muted: oklch(0.25 0.018 240);
--muted-foreground: oklch(0.68 0.018 220);
--accent: oklch(0.70 0.075 210);
--accent-foreground: oklch(0.17 0.015 240);
--destructive: oklch(0.65 0.18 25);
--destructive-foreground: oklch(0.17 0.015 240);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 14%);
--ring: oklch(0.70 0.075 210);
--chart-1: oklch(0.70 0.075 210);
--chart-2: oklch(0.65 0.060 195);
--chart-3: oklch(0.72 0.050 245);
--chart-4: oklch(0.60 0.060 175);
--chart-5: oklch(0.75 0.050 220);
--sidebar: oklch(0.19 0.017 240);
--sidebar-foreground: oklch(0.92 0.010 220);
--sidebar-primary: oklch(0.92 0.010 220);
--sidebar-primary-foreground: oklch(0.17 0.015 240);
--sidebar-accent: oklch(0.25 0.018 240);
--sidebar-accent-foreground: oklch(0.92 0.010 220);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.70 0.075 210);
}
/* =========================================================================
* 5. Nordic dark
*
* Dark-first palette inspired by polar-night greys with restrained frost-
* blue accents. Light variant included for cross-mode toggling but the
* theme reads best in dark mode. Suits: incident write-ups, debug dumps,
* developer tooling artefacts, anything that benefits from a low-glare
* dark surface.
*
* Suggested fonts:
* headings: "Geist", "Inter Tight", system-ui, sans-serif
* body: "Geist", "Inter Tight", system-ui, sans-serif
* mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace
* ========================================================================= */
:root {
--radius: 0.375rem;
--background: oklch(0.96 0.005 250);
--foreground: oklch(0.28 0.020 250);
--card: oklch(0.97 0.004 250);
--card-foreground: oklch(0.28 0.020 250);
--popover: oklch(0.97 0.004 250);
--popover-foreground: oklch(0.28 0.020 250);
--primary: oklch(0.34 0.025 250);
--primary-foreground: oklch(0.96 0.005 250);
--secondary: oklch(0.92 0.010 250);
--secondary-foreground: oklch(0.34 0.025 250);
--muted: oklch(0.92 0.010 250);
--muted-foreground: oklch(0.50 0.022 250);
--accent: oklch(0.60 0.080 220);
--accent-foreground: oklch(0.96 0.005 250);
--destructive: oklch(0.55 0.18 25);
--destructive-foreground: oklch(0.96 0.005 250);
--border: oklch(0.85 0.010 250);
--input: oklch(0.85 0.010 250);
--ring: oklch(0.60 0.080 220);
--chart-1: oklch(0.60 0.080 220);
--chart-2: oklch(0.55 0.070 170);
--chart-3: oklch(0.55 0.070 280);
--chart-4: oklch(0.65 0.100 60);
--chart-5: oklch(0.62 0.060 250);
--sidebar: oklch(0.94 0.008 250);
--sidebar-foreground: oklch(0.28 0.020 250);
--sidebar-primary: oklch(0.34 0.025 250);
--sidebar-primary-foreground: oklch(0.96 0.005 250);
--sidebar-accent: oklch(0.92 0.010 250);
--sidebar-accent-foreground: oklch(0.34 0.025 250);
--sidebar-border: oklch(0.85 0.010 250);
--sidebar-ring: oklch(0.60 0.080 220);
}
.dark {
--background: oklch(0.21 0.015 250);
--foreground: oklch(0.90 0.012 240);
--card: oklch(0.25 0.018 250);
--card-foreground: oklch(0.90 0.012 240);
--popover: oklch(0.25 0.018 250);
--popover-foreground: oklch(0.90 0.012 240);
--primary: oklch(0.90 0.012 240);
--primary-foreground: oklch(0.25 0.018 250);
--secondary: oklch(0.29 0.020 250);
--secondary-foreground: oklch(0.90 0.012 240);
--muted: oklch(0.29 0.020 250);
--muted-foreground: oklch(0.66 0.018 240);
--accent: oklch(0.72 0.080 220);
--accent-foreground: oklch(0.21 0.015 250);
--destructive: oklch(0.65 0.18 25);
--destructive-foreground: oklch(0.21 0.015 250);
--border: oklch(1 0 0 / 12%);
--input: oklch(1 0 0 / 14%);
--ring: oklch(0.72 0.080 220);
--chart-1: oklch(0.72 0.080 220);
--chart-2: oklch(0.68 0.070 170);
--chart-3: oklch(0.68 0.070 280);
--chart-4: oklch(0.72 0.100 60);
--chart-5: oklch(0.70 0.060 250);
--sidebar: oklch(0.23 0.017 250);
--sidebar-foreground: oklch(0.90 0.012 240);
--sidebar-primary: oklch(0.90 0.012 240);
--sidebar-primary-foreground: oklch(0.25 0.018 250);
--sidebar-accent: oklch(0.29 0.020 250);
--sidebar-accent-foreground: oklch(0.90 0.012 240);
--sidebar-border: oklch(1 0 0 / 12%);
--sidebar-ring: oklch(0.72 0.080 220);
}
#!/bin/bash
set -e
echo "Bundling React app to a single self-contained HTML file..."
if [ ! -f "package.json" ]; then
echo "Error: no package.json found. Run this from your project root."
exit 1
fi
if [ ! -f "index.html" ]; then
echo "Error: no index.html in project root."
exit 1
fi
echo "Installing bundling dependency (vite-plugin-singlefile)..."
pnpm add -D vite-plugin-singlefile
echo "Writing vite.config.bundle.ts (single-file build config)..."
cat > vite.config.bundle.ts << 'EOF'
import path from "path";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { viteSingleFile } from "vite-plugin-singlefile";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [react(), tailwindcss(), viteSingleFile()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
build: {
target: "esnext",
assetsInlineLimit: 100000000,
chunkSizeWarningLimit: 100000000,
cssCodeSplit: false,
reportCompressedSize: false,
rollupOptions: {
output: {
inlineDynamicImports: true,
},
},
},
});
EOF
echo "Cleaning previous bundle..."
rm -rf dist bundle.html
echo "Building..."
pnpm exec vite build --config vite.config.bundle.ts
if [ ! -f "dist/index.html" ]; then
echo "Error: build did not produce dist/index.html"
exit 1
fi
mv dist/index.html bundle.html
FILE_SIZE=$(du -h bundle.html | cut -f1)
echo ""
echo "Bundle complete."
echo "Output: bundle.html ($FILE_SIZE)"
echo ""
echo "Share this single file as a Claude artefact, or open it in a browser to preview locally."
#!/bin/bash
set -e
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
NODE_MINOR=$(node -v | cut -d'v' -f2 | cut -d'.' -f2)
echo "Detected Node.js $(node -v)"
if [ "$NODE_VERSION" -lt 20 ] || { [ "$NODE_VERSION" -eq 20 ] && [ "$NODE_MINOR" -lt 19 ]; }; then
echo "Error: Node.js 20.19+ or 22.12+ is required for Vite 8 + Tailwind v4."
exit 1
fi
if [[ "$OSTYPE" == "darwin"* ]]; then
SED_INPLACE="sed -i ''"
else
SED_INPLACE="sed -i"
fi
if ! command -v pnpm &> /dev/null; then
echo "pnpm not found. Installing..."
npm install -g pnpm
fi
if [ -z "$1" ]; then
echo "Usage: bash init-artifact.sh <project-name>"
exit 1
fi
PROJECT_NAME="$1"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPONENTS_TARBALL="$SCRIPT_DIR/shadcn-components.tar.gz"
if [ ! -f "$COMPONENTS_TARBALL" ]; then
echo "Error: shadcn-components.tar.gz not found at $COMPONENTS_TARBALL"
exit 1
fi
echo "Creating Vite React + TypeScript project: $PROJECT_NAME"
pnpm create vite@latest "$PROJECT_NAME" --template react-ts
cd "$PROJECT_NAME"
echo "Cleaning Vite template..."
$SED_INPLACE '/<link rel="icon".*vite\.svg/d' index.html
$SED_INPLACE 's/<title>.*<\/title>/<title>'"$PROJECT_NAME"'<\/title>/' index.html
rm -f src/App.css src/App.tsx
rm -rf src/assets
echo "Installing base dependencies..."
pnpm install
echo "Installing Tailwind v4, shadcn runtime, Radix UI and icons..."
pnpm add tailwindcss @tailwindcss/vite tw-animate-css \
class-variance-authority clsx tailwind-merge lucide-react \
radix-ui \
next-themes sonner cmdk vaul \
embla-carousel-react react-day-picker react-resizable-panels \
date-fns react-hook-form @hookform/resolvers zod \
input-otp
pnpm add -D @types/node
echo "Writing vite.config.ts with Tailwind v4 plugin and @ alias..."
cat > vite.config.ts << 'EOF'
import path from "path";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
EOF
echo "Adding path aliases to tsconfig.json..."
node -e "
const fs = require('fs');
const config = JSON.parse(fs.readFileSync('tsconfig.json', 'utf8'));
config.compilerOptions = config.compilerOptions || {};
config.compilerOptions.baseUrl = '.';
config.compilerOptions.paths = { '@/*': ['./src/*'] };
fs.writeFileSync('tsconfig.json', JSON.stringify(config, null, 2));
"
echo "Adding path aliases to tsconfig.app.json..."
node -e "
const fs = require('fs');
const path = 'tsconfig.app.json';
const content = fs.readFileSync(path, 'utf8');
const stripped = content.split('\n').filter(line => !line.trim().startsWith('//')).join('\n');
const config = JSON.parse(stripped.replace(/\/\*[\s\S]*?\*\//g, '').replace(/,(\s*[}\]])/g, '\$1'));
config.compilerOptions = config.compilerOptions || {};
config.compilerOptions.baseUrl = '.';
config.compilerOptions.paths = { '@/*': ['./src/*'] };
fs.writeFileSync(path, JSON.stringify(config, null, 2));
"
echo "Writing components.json..."
cat > components.json << 'EOF'
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
EOF
echo "Extracting pre-built shadcn components, hooks, lib, and index.css..."
tar -xzf "$COMPONENTS_TARBALL" -C src/
echo "Writing src/main.tsx..."
cat > src/main.tsx << 'EOF'
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
EOF
echo "Writing minimal src/App.tsx placeholder..."
cat > src/App.tsx << 'EOF'
export default function App() {
return (
<main className="min-h-svh">
<div className="mx-auto max-w-3xl px-6 py-12">
<h1 className="text-2xl font-semibold tracking-tight">
Replace this page with your artefact.
</h1>
<p className="mt-2 text-sm text-muted-foreground">
Edit <code>src/App.tsx</code> to begin.
</p>
</div>
</main>
);
}
EOF
echo ""
echo "Setup complete. Stack:"
echo " - React 19 + TypeScript"
echo " - Vite 8"
echo " - Tailwind CSS v4 (CSS-first config in src/index.css)"
echo " - shadcn/ui (new-york style, 40+ components in src/components/ui)"
echo " - lucide-react icons"
echo ""
echo "Pre-installed components:"
echo " accordion, alert, alert-dialog, aspect-ratio, avatar, badge,"
echo " breadcrumb, button, calendar, card, carousel, checkbox, collapsible,"
echo " command, context-menu, dialog, drawer, dropdown-menu, form,"
echo " hover-card, input, input-otp, label, menubar, navigation-menu,"
echo " pagination, popover, progress, radio-group, resizable, scroll-area,"
echo " select, separator, sheet, sidebar, skeleton, slider, sonner,"
echo " switch, table, tabs, textarea, toggle, toggle-group, tooltip"
echo ""
echo "To develop:"
echo " cd $PROJECT_NAME"
echo " pnpm dev"
echo ""
echo "Imports use the @/ alias, for example:"
echo " import { Button } from '@/components/ui/button'"
echo " import { Card, CardHeader } from '@/components/ui/card'"
echo " import { cn } from '@/lib/utils'"