| name | html-bloomberg-style |
| description | Bloomberg Terminal genre — data-dense, quantitative. Use when: financial report, earnings, valuation, portfolio, P&L, cashflow, dashboard, KPI report, metrics, performance/market/risk data, investment/trading memo, allocation, returns, yield. Trigger: financial report / earnings / valuation / portfolio / P&L / dashboard / KPI / metrics / data analysis / market data / investment memo / trading / 财报 / 估值 / 看板 / 数据. Inherits typography-system. NOT for: prose reports (economist/document). |
name: html-bloomberg-style
description: "Use when generating data-dense financial HTML documents: market reports, KPI dashboards, investment memos, portfolio summaries, earnings analyses. Trigger: document is primarily numerical/tabular with data density > narrative density. Routed by html-style-router — do not invoke directly unless router has already selected this style."
combo-tags: [html-rendering]
HTML Document Style -- Bloomberg Terminal (Financial Data)
Typography base (inherited): this skill composes ON TOP of the shared
typography-system foundation — link/inline
tokens/typography.css FIRST, then layer this skill for color + accent +
brand font-family ONLY. Never re-declare the type scale, vertical rhythm,
measure, page frame, or CJK rules; keep mixed CJK+Latin stacks Latin-first.
Verify: python3 scripts/audit-genre-typography.py.
The data-dense financial style for market reports, financial analyses,
risk dashboards, investment memos, and any document whose primary function
is presenting structured numerical data for rapid decision-making.
Apply this style when generating single-file HTML deliverables of type:
financial report, market analysis, investment memo, risk assessment,
portfolio review, earnings summary, valuation model output, KPI dashboard,
or any document where numerical data density exceeds narrative density.
For documentation/guides, use html-document-style (Claude).
For editorial reports/briefings, use html-economist-style (Economist).
For strategy/consulting deliverables, use html-mckinsey-style (McKinsey).
Universal Layout Frame (MUST inject before any other CSS)
Non-negotiable. Satisfies pre-tool-style-router-gate.py dual-verify (identifier +
canonical token signature --bloomberg-) AND prevents the hero/body misalignment
bug at wide viewports by sharing one reference frame across cover and body.
:root{
--bloomberg-page-max:1280px;
--bloomberg-page-gutter-total:clamp(24px,4vw,64px);
}
.frame{
width:min(var(--bloomberg-page-max),calc(100% - var(--bloomberg-page-gutter-total)));
margin-inline:auto;
}
.terminal-bar, .header-bar, .cover{padding-block:24px 32px;padding-inline:0;}
main.frame{padding-block:32px 64px;}
<header class="terminal-bar">
<div class="frame">
<span class="ticker">…</span>
</div>
</header>
<main class="frame">…</main>
Anti-pattern: .terminal-bar { padding-inline: 4vw } combined with
main { max-width: 1280px; margin: 0 auto } misaligns the ticker bar from the
data tables at >1480px viewports. Always wrap bar children in .frame.
Design Philosophy
- Dark canvas: Dark backgrounds reduce eye strain for data-heavy reading
- Data-first: Numbers are the content; prose is annotation
- Signal colors: Green/red/amber carry meaning, not decoration
- Monospace data: Numerical alignment requires fixed-width rendering
- Terminal heritage: Respects the Bloomberg Terminal's information density
- Glow, not shadow: Subtle luminous borders replace paper shadows
Color Palette (CSS Custom Properties)
:root {
--bg-deep: #0A0E17;
--bg-panel: #111827;
--bg-card: #1A2233;
--bg-hover: #1F2A3D;
--bg-input: #0D1321;
--border: rgba(255,255,255,0.06);
--border-strong: rgba(255,255,255,0.12);
--border-glow: rgba(255,159,28,0.15);
--text-primary: #E8E8ED;
--text-secondary: #9CA3B0;
--text-muted: #5C6370;
--text-label: #6B7385;
--green: #22C55E;
--green-dim: rgba(34,197,94,0.12);
--red: #EF4444;
--red-dim: rgba(239,68,68,0.12);
--amber: #FF9F1C;
--amber-dim: rgba(255,159,28,0.10);
--blue: #3B82F6;
--blue-dim: rgba(59,130,246,0.10);
--accent: #FF9F1C;
--accent-dim: rgba(255,159,28,0.08);
--font-data: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Menlo', monospace;
--font-ui: 'Inter', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
Typography Rules
Font Loading (Google Fonts)
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
Key: Dual Mono + Sans System
All numerical data uses monospace; all UI text uses sans-serif.
No serif fonts anywhere in this style.
| Element | Font | Weight | Size | Color | Usage |
|---|
| Page title | --font-ui | 800 | 2rem | --text-primary | Document title |
| Section title | --font-ui | 700 | 1.3rem | --text-primary | Section headings |
| Body text | --font-ui | 400 | 14px | --text-secondary | Descriptions |
| Data values | --font-data | 600 | 1.1rem+ | --accent or signal | KPIs, prices, metrics |
| Table cells | --font-data | 400 | 13px | --text-primary | Tabular data |
| Labels | --font-ui | 600 | 11px | --text-label | Uppercase data labels |
| Ticker/code | --font-data | 500 | 13px | --accent | Tickers, identifiers |
Dark Mode CJK
body {
-webkit-font-smoothing: antialiased;
font-variant-numeric: tabular-nums;
}
Structural Elements
Top Bar (Ticker-Style)
.top-bar {
background: var(--bg-panel);
border-bottom: 1px solid var(--border);
padding: 10px 0;
font-family: var(--font-data);
font-size: 12px;
color: var(--text-muted);
display: flex;
justify-content: space-between;
}
.top-bar .amber { color: var(--accent); }
Hero (Dark, Minimal)
.hero {
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: 4px;
padding: 40px 36px;
}
.hero h1 {
font-family: var(--font-ui);
font-weight: 800;
font-size: 2rem;
color: var(--text-primary);
margin-bottom: 12px;
}
.hero .subtitle {
color: var(--text-secondary);
font-size: 0.95rem;
}
Data Panels (Primary Content Unit)
Replace "cards" with "panels" -- dark surfaces with subtle borders:
.panel {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 4px;
padding: 20px;
}
.panel:hover {
border-color: var(--border-strong);
}
.panel-header {
font-family: var(--font-ui);
font-size: 11px;
font-weight: 600;
color: var(--text-label);
text-transform: uppercase;
letter-spacing: 1.5px;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border);
}
KPI Grid (Data Prominence)
.kpi-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.kpi {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 4px;
padding: 20px;
}
.kpi .label {
font-family: var(--font-ui);
font-size: 11px;
font-weight: 600;
color: var(--text-label);
text-transform: uppercase;
letter-spacing: 1.5px;
margin-bottom: 8px;
}
.kpi .value {
font-family: var(--font-data);
font-size: 1.8rem;
font-weight: 700;
color: var(--accent);
line-height: 1.1;
}
.kpi .change {
font-family: var(--font-data);
font-size: 13px;
margin-top: 6px;
}
.kpi .change.up { color: var(--green); }
.kpi .change.down { color: var(--red); }
.kpi .change.flat { color: var(--text-muted); }
Tables (Data-Dense, Terminal Feel)
table {
width: 100%;
border-collapse: collapse;
font-family: var(--font-data);
font-size: 13px;
}
th {
background: var(--bg-panel);
color: var(--text-label);
font-family: var(--font-ui);
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 10px 14px;
border-bottom: 1px solid var(--border-strong);
text-align: left;
}
th.num, td.num {
text-align: right;
font-variant-numeric: tabular-nums;
}
td {
padding: 8px 14px;
border-bottom: 1px solid var(--border);
color: var(--text-primary);
}
tr:hover td { background: var(--bg-hover); }
td.positive { color: var(--green); }
td.negative { color: var(--red); }
td.neutral { color: var(--amber); }
Signal Tags
.signal {
display: inline-block;
font-family: var(--font-data);
font-size: 11px;
font-weight: 600;
padding: 3px 10px;
border-radius: 3px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.signal.buy { background: var(--green-dim); color: var(--green); }
.signal.sell { background: var(--red-dim); color: var(--red); }
.signal.hold { background: var(--amber-dim); color: var(--amber); }
.signal.info { background: var(--blue-dim); color: var(--blue); }
Callouts (Glow Border)
.callout {
background: var(--bg-card);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
padding: 16px 20px;
border-radius: 4px;
font-size: 0.9rem;
color: var(--text-secondary);
}
.callout-risk {
border-left-color: var(--red);
background: var(--red-dim);
}
.callout-positive {
border-left-color: var(--green);
background: var(--green-dim);
}
Metric Bars (Thin, Colored)
.metric-bar {
height: 4px;
background: rgba(255,255,255,0.06);
border-radius: 2px;
overflow: hidden;
}
.metric-bar > span {
display: block;
height: 100%;
border-radius: 2px;
}
.metric-bar .fill-green { background: var(--green); }
.metric-bar .fill-red { background: var(--red); }
.metric-bar .fill-amber { background: var(--amber); }
.metric-bar .fill-blue { background: var(--blue); }
Layout Principles
- Max-width: full viewport or 1200px (data needs width)
- Padding: 24px horizontal (dense)
- Section spacing: 24px between sections (compact)
- Border-radius: 4px (minimal, functional -- not 0 like Economist, not 10 like Claude)
- Shadows: None; use border-glow for emphasis states
- Background: Dark throughout (--bg-deep for page, --bg-panel/--bg-card for surfaces)
- Density: Maximum -- more data per viewport than any other style
Grid Rules
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 960px) {
.grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
.grid-2 { grid-template-columns: 1fr; }
.grid-3, .grid-4 { grid-template-columns: 1fr; }
.kpi-grid { grid-template-columns: repeat(2, 1fr); }
}
Footer
<footer class="site-footer">
<p>[ProjectName] -- [Document Type]</p>
<p>Your Name | your@email.com</p>
<p class="attr">[Data sources + timestamp + refresh frequency]</p>
</footer>
Footer uses --text-muted on --bg-panel. Always include data timestamp.
Anti-Patterns (Never Do)
- No light/white page background (dark mode is the identity)
- No serif fonts (mono + sans only)
- No decorative colors (colors MUST carry data meaning)
- No large border-radius (max 4px)
- No box shadows (use border-glow if needed)
- No warm tones (cool palette only)
- No low-density layouts (maximize data per viewport)
- No emoji (per project rules)
- No prose-heavy sections without data (every section should have numbers)
- No grid columns > 4 for CJK text-heavy cards (Chinese text in 5+ columns overflows on 1200px container; use max
repeat(4, 1fr) for KPI cards, repeat(3, 1fr) for text-heavy pillars)
- No
word-break: break-all on body (conflicts with CJK keep-all; use word-break: keep-all; overflow-wrap: break-word)
- No fixed-width grids without overflow protection (add
min-width: 0 or overflow: hidden to grid children)
- No
table.data without word-break: break-word + overflow-wrap: anywhere (CJK paragraphs without whitespace cannot be auto-wrapped by browser default; without these properties one long cell stretches its column beyond panel/viewport. See Gotcha #4)
- No
table.data with table-layout: fixed as global default (forces all columns to 1/N equal width — short cells like ID/badges waste massive horizontal space. fixed is opt-in only via class="data fixed" for tables with <colgroup> widths AND known long-content cells >100 CJK chars. Default is auto so browser sizes columns to content. See Gotcha #4)
- No
.panel without overflow-x: auto (last-line defense — if a child still overflows despite table fixes, panel gets a horizontal scrollbar instead of silently clipping content off the right edge)
Comparison with Other Styles
| Dimension | Claude | Economist | McKinsey | Bloomberg |
|---|
| Page bg | Cream | White | White+gray | Dark #0A0E17 |
| Accent | Terracotta | Red | Deep Blue | Amber #FF9F1C |
| Headlines | Serif | Sans | Sans (heavy) | Sans (medium) |
| Body | Sans | Serif | Sans | Sans |
| Data font | -- | -- | Sans | Monospace |
| Corners | 10px | 0px | 0px | 4px |
| Shadows | Paper tiers | None | None | Glow borders |
| Density | Spacious | Dense | Structured | Maximum |
| Container | 1100px | 1100px | 1100px | 1200px/full |
| Feel | Academic book | Newspaper | Boardroom | Trading floor |
Gotchas
-
Terminal/dashboard text uses opacity and becomes unreadable on dark backgrounds. Never use opacity for text contrast — use explicit color: rgba(255,255,255,0.85) minimum for secondary text on dark panels. The dark terminal background (#0A0A0F or similar) requires careful contrast management. This is a cross-style-system rule validated on 2026-04-09.
-
Data grid cells and code blocks need generous internal padding (>=16px). Content touching container borders destroys readability in data-dense Bloomberg-style documents. Minimum: padding: 16px 20px for data cells, padding: 20px 24px for code blocks.
-
Container max-width consistency across style systems. Bloomberg uses wider containers (1200px/full) but when generating standard documents (not dashboards), use max-width: 1100px for body content to stay consistent with other styles. Apply to all max-width-constrained elements uniformly.
-
CJK overflow防御 = 两层策略:universal wrap properties + opt-in fixed layout. Naively setting table-layout: fixed as a global default backfires — without <colgroup> widths, all columns become 1/N equal-width, so short ID/badge cells waste massive horizontal space (a 4-column risk table renders R1, HIGH-badge, prose, prose all at 25% — the prose columns get less width than they need while ID gets 5x more than it needs). Validated empirically twice on 2026-04-30: round 1, no defense → §10.5 row with ~1,200 chars overflowed beyond viewport, 预计 column clipped, headers appeared overlapped; round 2, global table-layout: fixed over-corrected → §07 risk table rendered with massive whitespace because R1/badges/short cells each got 25%.
Correct universal CSS (applies to ALL <table class="data">, no exceptions):
.panel { overflow-x: auto; }
table.data {
width: 100%;
table-layout: auto;
word-break: break-word;
overflow-wrap: anywhere;
}
table.data.fixed {
table-layout: fixed;
}
table.data th, table.data td {
vertical-align: top;
white-space: normal;
}
When to opt into class="data fixed" (require BOTH conditions):
- At least one column is expected to hold >100 CJK chars (status updates, RCA descriptions, regulation summaries)
- You can supply explicit
<colgroup> widths for ALL columns
Opt-in pattern:
<table class="data fixed">
<colgroup>
<col style="width: 8%">
<col style="width: 12%">
<col style="width: 25%">
<col style="width: 45%">
<col style="width: 10%">
</colgroup>
...
</table>
Failure modes this prevents:
- Silent CJK overflow (round 1 anti-pattern): the table looks fine on initial render with short rows, then a later edit adds a 1000-char description and the layout breaks invisibly (no error, just visually clipped right side). Universal
word-break: break-word + overflow-wrap: anywhere prevent this WITHOUT needing table-layout: fixed.
- Equal-width waste (round 2 anti-pattern): forcing
table-layout: fixed globally collapses information density — single-glyph ID cells get the same width as multi-paragraph description cells. auto default + fixed opt-in solves both.
Decision rule (one-liner): use class="data" by default; upgrade to class="data fixed" only when adding a <colgroup> because one column has >100 CJK chars expected. The wrap properties handle 90% of overflow without ever needing fixed layout.