Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Penguin visual language for generated web pages and app UIs — GitHub-style simplicity with a single blue accent, light and pure-black dark themes, design tokens, component and chat-interface recipes, plus an opt-in warm paper editorial theme.
.claude/skills/prism-shadow-web-design/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 54% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 141% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 130% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 364% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 115% | 0% |
Default visual language for every web page or frontend you generate, distilled from the Penguin Harness landing page and web app. The idea is GitHub-style simplicity: solid backgrounds, 1px borders instead of shadows, system fonts, one blue accent used sparingly. Depth comes from hairline borders, not shadows or gradients; dark mode is pure black, not navy. Apply these defaults unless the user explicitly asks for another style. One packaged alternative exists — the paper editorial theme below, for requests that call for a warm, print-like feel; pick one language per product and never mix them. The typography discipline, language, motion, IME, citation and responsiveness rules apply under both. Treat the user's one-line request as the whole spec: this skill fills every unstated gap, so the result is a finished page — never a wireframe that waits for styling feedback.
If the user's message only invokes this skill (e.g. "use web-design skill") without a concrete page or interface to build, ask what they want to build. When a concrete build is already requested (an app UI, a landing page, a RAG chat interface), do not ask about styling — colors, fonts, spacing, radii and layout are all decided by the defaults below; the only question ever worth asking is what to build, never how it should look. Route by request shape: conversational or docs-QA → the chat/RAG layout; product or marketing → the page layout; tool-like apps → a sticky nav + panels composed from the components.
Non-negotiable for ANY text input that sends on Enter: never send while an IME composition is in progress (check event.isComposing, falling back to event.keyCode === 229, on keydown). For Chinese/Japanese/Korean input methods, that Enter only confirms the composed text — auto-sending on it fires half-typed messages. Details in the composer recipe below.
Every delivery, even from a one-line request, includes all of this unasked:
<html lang> matching the UI language; <meta name="viewport" content="width=device-width, initial-scale=1">; a real <title>.:focus-visible, Esc closes overlays (focus returning to the trigger), Enter submits (IME-safe as above).alt text on images, aria-label on icon-only buttons; tap targets ≥ 40px.css:root { color-scheme: light; /* Brand blue — the only accent family. Use sparingly: links, eyebrows, tiny dots, tints. */ --brand-50: #e8f0fe; --brand-100: #d2e3fc; --brand-300: #8ab4f8; --brand-500: #4285f4; --brand-600: #1a73e8; /* accent text/icons in light mode */ --brand-700: #0b57d0; /* links on white */ /* Neutrals (Tailwind gray) */ --gray-50: #f9fafb; --gray-100: #f3f4f6; --gray-200: #e5e7eb; --gray-300: #d1d5db; --gray-400: #9ca3af; --gray-500: #6b7280; --gray-600: #4b5563; --gray-900: #111827; --bg: #ffffff; --surface: #ffffff; --border: var(--gray-200); --control-border: var(--gray-300); --fg: var(--gray-900); --fg-muted: var(--gray-600); --fg-faint: var(--gray-500); --accent-bg: #111827; --accent-fg: #ffffff; /* primary buttons are near-black, not blue */ --radius-control: 6px; /* buttons, inputs, chips */ --radius-card: 12px; /* cards, panels */ --ease: cubic-bezier(0.2, 0.7, 0.3, 1); } .dark { color-scheme: dark; /* pure black, no blue tint */ --bg: #000000; --surface: #0d0d0d; --border: #1f1f1f; --control-border: #303030; --fg: #f3f4f6; --fg-muted: #9ca3af; --fg-faint: #6b7280; --accent-bg: #f3f4f6; --accent-fg: #111827; /* primary button inverts to light */ --brand-600: #8ab4f8; --brand-700: #8ab4f8; /* brand text flips to the 300 tone */ }
dark class on <html> (persist the choice; default to prefers-color-scheme).--brand-50 tinted chips.border-radius: 9999px; everything else uses the two radii above. No gradients or elevation shadows on content (modals excepted; flat focus rings drawn with box-shadow are fine).System fonts only — no CDN fonts, no @font-face. The CJK entries matter (bilingual product):
cssbody { font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif; -webkit-font-smoothing: antialiased; } code, pre, kbd { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
Headings are always font-weight: 600 with letter-spacing: -0.025em — nothing heavier, no thin weights. Scale: page/hero title 30–36px; section title 24–30px; card title 16–18px; body 14px / line-height 1.5; captions 12px in --fg-faint; code 13px / line-height 1.85. Hierarchy comes from weight, size, spacing and hairlines — never from colored blocks.
Write the page's UI copy in the language the user's request was made in — a Chinese request gets a Chinese interface, an English request an English one — and set <html lang> to match. Code identifiers, CSS class names and code comments stay English.
background: var(--accent-bg); color: var(--accent-fg); border-radius: 6px; padding: 6px 12px; font-size: 14px; font-weight: 500; hover: opacity: .9. Large CTA variant: height 44px, radius 8px, padding 0 20px.--surface bg, 1px solid var(--control-border), same paddings; hover swaps bg to --gray-100/dark #1f1f1f.border: 1px solid var(--border); border-radius: 12px; background: var(--surface); padding: 24px; hover changes only the border color (one step darker) — no lift, shadow or scale.border: 1px solid var(--control-border); border-radius: 6px; padding: 8px 12px; focus: border one step darker + box-shadow: 0 0 0 2px rgb(156 163 175 / .3); no default outline stacking.border-radius: 9999px; border: 1px solid var(--border); padding: 4px 10px; font-size: 12px; color: var(--fg-muted); hover: bg --gray-50. Brand-tinted variant: --brand-50 bg, --brand-700 text.height: 56px; border-bottom: 1px solid var(--border); background: color-mix(in srgb, var(--bg) 85%, transparent); backdrop-filter: blur(8px); logo 28px + product name 15px semibold.--gray-50/dark --surface bg card with a bordered header row (mono 12px label + copy button), body mono 13px.:focus-visible { outline: 3px solid rgb(107 114 128 / .4); outline-offset: 2px; }.One easing everywhere: var(--ease), durations 120–280ms. Entrances rise in (translateY(10px) + fade, 280ms, stagger siblings by 40ms); overlays fade (120ms); hover feedback is color-only (transition: color, background-color, border-color 150ms) — never transform on hover. Always include:
css@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
A second complete language — warm paper tones, serif display headings, mono micro-labels — for when the user asks for a warm, editorial, print- or magazine-like feel. Light-only: if the product needs dark mode, use the default language instead.
css:root { color-scheme: light; --paper: #f7f4ee; /* page bg */ --panel: #eeebe4; /* side panels */ --card: #fffdf9; --ink: #262421; --muted: #716d67; --line: #ded9d0; /* warm hairlines */ --accent: #d6663f; --accent-deep: #9e462b; /* the only accent; deep tone for text/links */ --live: #547567; /* status green — live dots and retrieval notes only */ }
Georgia, "Times New Roman", "Songti SC", "Noto Serif SC", serif; still no CDN fonts), weight 400–500, letter-spacing: -.04em, hero clamp(38px, 5vw, 62px), with exactly one word wrapped in an accent-colored <em>. Body text stays on the default sans stack, 13–14px / line-height 1.7.01), status values and citation numbers: 9–11px uppercase monospace, letter-spacing: .1–.18em, in --accent-deep or --muted. The serif-vs-mono contrast is this theme's hierarchy tool, replacing the default theme's weight-and-size ladder.box-shadow: 0 14px 40px rgb(65 50 39 / .09); example-card hover may lift translateY(-2px) and gain 0 10px 28px rgb(62 49 40 / .07).--accent square with one tight corner (border-radius: 11px 11px 11px 4px) holding a white serif initial; user chat bubbles echo it with border-radius: 3px 14px 14px 14px. Empty-state flourish: that mark in a circular medallion ringed by 1–2 offset half-circle hairlines (clip-path: inset(0 50% 0 0)) — this theme's counterpart of the default dot grid.--accent fill (hover --accent-deep; icon-only send buttons go circular); secondary stays a hairline border on --card/white. Links use --accent-deep.The default shape for a generated conversational or docs-QA app:
max-width: 48rem, padding: 0 16px; sticky nav on top with the app name; message list grows, composer pinned at the bottom. A docs-QA app whose index is worth showing may add a left knowledge panel (grid 310px 1fr under the nav; panel-toned bg --gray-50 / paper --panel, 1px right border): kicker, display title, one-paragraph description, an index-status block of label-vs-mono-value rows (docs, chunks, last synced, a LIVE dot), and a one-line pipeline (corpus → retrieval → cited answer); the chat pane keeps its own centered column. On mobile the panel hides behind an ⓘ button in the nav and drops down fixed beneath it.--accent-deep) → title with at most one accent word → one-line subtitle in --fg-muted, over the theme flourish (default: dot-grid backdrop background-image: radial-gradient(rgb(26 115 232 / .14) 1px, transparent 1px); background-size: 22px 22px; faded out with a bottom mask; paper: the orbit medallion) — the only decorative flourish allowed. Below it, 3–4 example questions the app can genuinely answer, as pill chips or as a 2-column grid (1-column mobile) of numbered cards — mono 01 in accent, the question at 13px, a ↗ corner affordance; hover tints the border (paper may also lift 2px). Clicking one fills and submits the composer.--gray-100/dark #1f1f1f rounded bubble (radius 12px, padding 8px 14px, max-width 85%); assistant messages plain on the page background, no bubble. Stream deltas into the assistant message as they arrive with a 1-character pulsing cursor. Prefer a plain-text output contract over a Markdown pipeline: when the app controls its model's prompt, instruct plain-text answers and style them directly (escape → blank-line paragraphs → decorate [n] markers); build a Markdown renderer only when the output format isn't yours to set, and then escape HTML in the model text before your own transforms — never inject it raw. Retrieval-backed answers may open with a small status line above the text — breathing dot + "已检索 N 个相关片段" / "N sources matched", 11px in --live/--fg-faint — so evidence visibly precedes prose.--fg-muted content behind a hairline left border, expanded while it streams, auto-collapsed the moment the first answer delta arrives (reopenable). Never restyle thinking as answer prose and never cite from it; when the model emits none, no placeholder space appears.[n] markers render as small raised accent superscripts (mono, ~10px). After the answer, either surface works, but it must reveal both the verbatim original text block and a link to the full source — a citation that is only a label or only a link is not enough: (a) a wrapped row of brand-tinted pill chips [1] path — heading opening a popover/panel on click; or (b) an accordion of source cards under the answer — each row a tinted numbered circle + doc title + section + +/− chevron, expanding to the verbatim excerpt as a mono blockquote (accent left border, max-height ≈150px, scrollable) plus the source link.Enter send · Shift+Enter newline, 11px muted, hidden on mobile), the primary send button on the right (paper: circular accent icon button); Enter sends, Shift+Enter for newline; disable while streaming. A short gradient from transparent into the page bg eases the list into the composer zone; below it, one 10–11px muted disclaimer line says what answers are based on. Never send while an IME composition is in progress: on keydown, ignore Enter when event.isComposing (or event.keyCode === 229) — for CJK input methods that Enter only confirms the composed text, and auto-sending on it fires half-typed messages.×; send attaches and clears the queue. In sent user messages, thumbnails render at max-height ~160px, radius 8px, click to view full size.--fg-faint; error: 13px #b91c1c text on #fef2f2 (dark: #f87171 on #450a0a) in a rounded box with a retry affordance. Never leave a silent failure.Content width max-width: 72rem, gutters 16–24px; section rhythm padding: 64–96px 0; section header = uppercase 14px semibold brand-colored eyebrow → title → one-line subtitle in --fg-muted, then a card grid (gap: 20px, 2–3 columns, collapsing to one on mobile). Hero: centered, logo + name, headline with at most one brand-colored word, CTA pair (primary + secondary). Responsive by default: single column under 640px, tap targets ≥ 40px, no horizontal scroll.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 46,952 | 43,247 | -8% | 1 | 1 | 0% | 8,309 | 12,789 | +54% | 0 | 0 | — |
case-02 | fail→pass | 42,656 | 36,690 | -14% | 1 | 1 | 0% | 8,303 | 12,783 | +54% | 0 | 0 | — |
case-03 | fail→fail | 48,319 | 44,350 | -8% | 1 | 1 | 0% | 8,294 | 12,774 | +54% | 0 | 0 | — |
case-04 | pass→pass | 13,578 | 36,177 | +166% | 1 | 1 | 0% | 2,745 | 12,708 | +363% | 0 | 0 | — |
case-12 | pass→pass | 15,004 | 12,941 | -14% | 1 | 1 | 0% | 1,821 | 5,910 | +225% | 0 | 0 | — |
case-13 | pass→pass | 17,752 | 16,584 | -7% | 1 | 1 | 0% | 2,413 | 6,819 | +183% | 0 | 0 | — |
case-14 | pass→pass | 18,511 | 39,409 | +113% | 1 | 1 | 0% | 4,400 | 12,695 | +189% | 0 | 0 | — |
case-10 | pass→fail | 22,716 | 35,495 | +56% | 1 | 1 | 0% | 4,376 | 12,420 | +184% | 0 | 0 | — |
case-11 | fail→pass | 26,669 | 16,464 | -38% | 1 | 1 | 0% | 3,384 | 8,159 | +141% | 0 | 0 | — |
case-05 | pass→pass | 15,120 | 36,664 | +142% | 1 | 1 | 0% | 2,220 | 12,687 | +471% | 0 | 0 | — |
case-06 | pass→fail | 15,590 | 43,880 | +181% | 1 | 1 | 0% | 3,195 | 12,695 | +297% | 0 | 0 | — |
case-07 | pass→fail | 24,675 | 40,953 | +66% | 1 | 1 | 0% | 4,005 | 12,692 | +217% | 0 | 0 | — |
case-08 | pass→pass | 16,335 | 38,799 | +138% | 1 | 1 | 0% | 3,001 | 12,544 | +318% | 0 | 0 | — |
case-09 | fail→pass | 24,468 | 15,277 | -38% | 1 | 1 | 0% | 2,999 | 6,885 | +130% | 0 | 0 | — |
case-15 | fail→pass | 19,021 | 45,121 | +137% | 1 | 1 | 0% | 2,736 | 12,690 | +364% | 0 | 0 | — |
case-16 | fail→pass | 30,332 | 36,667 | +21% | 1 | 1 | 0% | 5,890 | 12,689 | +115% | 0 | 0 | — |
case-17 | pass→pass | 32,760 | 39,722 | +21% | 1 | 1 | 0% | 4,305 | 12,689 | +195% | 0 | 0 | — |
case-18 | pass→fail | 24,230 | 50,195 | +107% | 1 | 1 | 0% | 5,053 | 12,685 | +151% | 0 | 0 | — |
case-19 | fail→pass | 16,452 | 16,634 | +1% | 1 | 1 | 0% | 2,668 | 7,101 | +166% | 0 | 0 | — |
case-20 | fail→pass | 24,062 | 5,889 | -76% | 1 | 1 | 0% | 3,752 | 5,743 | +53% | 0 | 0 | — |
case-21 | pass→pass | 23,617 | 52,177 | +121% | 1 | 1 | 0% | 5,284 | 12,692 | +140% | 0 | 0 | — |
case-22 | fail→pass | 22,681 | 35,286 | +56% | 1 | 1 | 0% | 3,763 | 12,686 | +237% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.