refactor liveline: extract Section, List, Li components

Simplifies article content by extracting repeated patterns into
reusable components:
- Section: wraps Divider + SectionHeading + children
- List: styled ul with typography inline styles
- Li: styled li with correct padding

Article JSX now reads as clean declarative markup instead of
verbose inline-styled HTML.
This commit is contained in:
Tommy D. Rossi
2026-02-19 10:04:45 +01:00
parent a8481af89b
commit 5a3acd9cb8
+70 -63
View File
@@ -287,6 +287,39 @@ function Divider() {
); );
} }
function Section({ id, title, children }: { id: string; title: string; children: React.ReactNode }) {
return (
<>
<Divider />
<SectionHeading id={id}>{title}</SectionHeading>
{children}
</>
);
}
function List({ children }: { children: React.ReactNode }) {
return (
<ul
className="m-0 pl-5"
style={{
fontFamily: "var(--ll-font-primary)",
fontSize: "14px",
fontWeight: 475,
lineHeight: "20px",
letterSpacing: "-0.09px",
color: "var(--ll-text-primary)",
listStyleType: "disc",
}}
>
{children}
</ul>
);
}
function Li({ children }: { children: React.ReactNode }) {
return <li style={{ padding: "0 0 8px 12px" }}>{children}</li>;
}
function CodeBlock({ children, lang = "jsx" }: { children: string; lang?: string }) { function CodeBlock({ children, lang = "jsx" }: { children: string; lang?: string }) {
const codeRef = useRef<HTMLElement>(null); const codeRef = useRef<HTMLElement>(null);
const lines = children.split("\n"); const lines = children.split("\n");
@@ -618,10 +651,7 @@ export default function LivelinePage() {
Everything else is opt-in. Everything else is opt-in.
</Paragraph> </Paragraph>
<Divider /> <Section id="getting-started" title="Getting started">
{/* Getting started */}
<SectionHeading id="getting-started">Getting started</SectionHeading>
<CodeBlock lang="bash">npm install liveline</CodeBlock> <CodeBlock lang="bash">npm install liveline</CodeBlock>
@@ -662,10 +692,9 @@ function Chart({ data, value }) {
Resting heart rate. Custom formatter, exaggerated Y-axis. Resting heart rate. Custom formatter, exaggerated Y-axis.
</Caption> </Caption>
<Divider /> </Section>
{/* Momentum */} <Section id="momentum" title="Momentum">
<SectionHeading id="momentum">Momentum</SectionHeading>
<Paragraph> <Paragraph>
The <InlineCode>momentum</InlineCode> prop adds directional arrows The <InlineCode>momentum</InlineCode> prop adds directional arrows
@@ -682,10 +711,9 @@ function Chart({ data, value }) {
Arrows fade out fully before the new direction fades in. Arrows fade out fully before the new direction fades in.
</Caption> </Caption>
<Divider /> </Section>
{/* Value overlay */} <Section id="value-overlay" title="Value overlay">
<SectionHeading id="value-overlay">Value overlay</SectionHeading>
<Paragraph> <Paragraph>
<InlineCode>showValue</InlineCode> renders the current value as a <InlineCode>showValue</InlineCode> renders the current value as a
@@ -698,10 +726,9 @@ function Chart({ data, value }) {
<ChartPlaceholder height={204} label="$17,138.03" /> <ChartPlaceholder height={204} label="$17,138.03" />
<Caption>60fps value overlay with momentum colouring.</Caption> <Caption>60fps value overlay with momentum colouring.</Caption>
<Divider /> </Section>
{/* Time windows */} <Section id="time-windows" title="Time windows">
<SectionHeading id="time-windows">Time windows</SectionHeading>
<Paragraph> <Paragraph>
Pass a <InlineCode>windows</InlineCode> array to render time horizon Pass a <InlineCode>windows</InlineCode> array to render time horizon
@@ -728,10 +755,9 @@ function Chart({ data, value }) {
CPU usage with occasional spikes. Rounded time windows. CPU usage with occasional spikes. Rounded time windows.
</Caption> </Caption>
<Divider /> </Section>
{/* Reference line */} <Section id="reference-line" title="Reference line">
<SectionHeading id="reference-line">Reference line</SectionHeading>
<Paragraph> <Paragraph>
<InlineCode>referenceLine</InlineCode> draws a horizontal line at a <InlineCode>referenceLine</InlineCode> draws a horizontal line at a
@@ -745,10 +771,9 @@ function Chart({ data, value }) {
$67,500?&quot; $67,500?&quot;
</Caption> </Caption>
<Divider /> </Section>
{/* Orderbook */} <Section id="orderbook" title="Orderbook">
<SectionHeading id="orderbook">Orderbook</SectionHeading>
<Paragraph> <Paragraph>
Pass an <InlineCode>orderbook</InlineCode> prop with{" "} Pass an <InlineCode>orderbook</InlineCode> prop with{" "}
@@ -771,10 +796,9 @@ function Chart({ data, value }) {
the price line. the price line.
</Caption> </Caption>
<Divider /> </Section>
{/* Theming */} <Section id="theming" title="Theming">
<SectionHeading id="theming">Theming</SectionHeading>
<Paragraph> <Paragraph>
Pass any CSS colour string to <InlineCode>color</InlineCode> and Pass any CSS colour string to <InlineCode>color</InlineCode> and
@@ -786,52 +810,39 @@ function Chart({ data, value }) {
<ChartPlaceholder height={204} label="540.42" /> <ChartPlaceholder height={204} label="540.42" />
<Caption>Dark theme. Same component, different colour.</Caption> <Caption>Dark theme. Same component, different colour.</Caption>
<Divider /> </Section>
{/* More features */} <Section id="more-features" title="More features">
<SectionHeading id="more-features">More features</SectionHeading>
<Paragraph> <Paragraph>
Everything is off by default or has sensible defaults. A few more Everything is off by default or has sensible defaults. A few more
things you can turn on: things you can turn on:
</Paragraph> </Paragraph>
<ul <List>
className="m-0 pl-5" <Li>
style={{
fontFamily: "var(--ll-font-primary)",
fontSize: "14px",
fontWeight: 475,
lineHeight: "20px",
letterSpacing: "-0.09px",
color: "var(--ll-text-primary)",
listStyleType: "disc",
}}
>
<li style={{ padding: "0 0 8px 12px" }}>
<InlineCode>exaggerate</InlineCode> tightens the Y-axis range so <InlineCode>exaggerate</InlineCode> tightens the Y-axis range so
small movements fill the full chart height. Useful for values that small movements fill the full chart height. Useful for values that
move in tiny increments, like the heart rate demo above. move in tiny increments, like the heart rate demo above.
</li> </Li>
<li style={{ padding: "0 0 8px 12px" }}> <Li>
<InlineCode>scrub</InlineCode> shows a crosshair with time and <InlineCode>scrub</InlineCode> shows a crosshair with time and
value tooltips on hover. On by default. value tooltips on hover. On by default.
</li> </Li>
<li style={{ padding: "0 0 8px 12px" }}> <Li>
<InlineCode>degen</InlineCode> enables burst particles and chart <InlineCode>degen</InlineCode> enables burst particles and chart
shake on momentum swings. For when subtlety is not the goal. shake on momentum swings. For when subtlety is not the goal.
</li> </Li>
<li style={{ padding: "0 0 8px 12px" }}> <Li>
<InlineCode>badgeVariant=&quot;minimal&quot;</InlineCode> renders a <InlineCode>badgeVariant=&quot;minimal&quot;</InlineCode> renders a
quieter white pill instead of the accent-colored default. Or{" "} quieter white pill instead of the accent-colored default. Or{" "}
<InlineCode>{"badge={false}"}</InlineCode> to remove it entirely. <InlineCode>{"badge={false}"}</InlineCode> to remove it entirely.
</li> </Li>
</ul> </List>
<Divider /> </Section>
{/* How it works */} <Section id="how-it-works" title="How it works">
<SectionHeading id="how-it-works">How it works</SectionHeading>
<Paragraph> <Paragraph>
One <InlineCode>{"<canvas>"}</InlineCode>, one{" "} One <InlineCode>{"<canvas>"}</InlineCode>, one{" "}
@@ -844,10 +855,9 @@ function Chart({ data, value }) {
of parts updating independently. of parts updating independently.
</Paragraph> </Paragraph>
<Divider /> </Section>
{/* Props */} <Section id="props" title="Props">
<SectionHeading id="props">Props</SectionHeading>
<PropsTable <PropsTable
title="Required" title="Required"
@@ -925,10 +935,9 @@ function Chart({ data, value }) {
]} ]}
/> />
<Divider /> </Section>
{/* Stress testing */} <Section id="stress-testing" title="Stress testing">
<SectionHeading id="stress-testing">Stress testing</SectionHeading>
<Paragraph> <Paragraph>
A chart that only looks good on calm data isn&apos;t much use. These A chart that only looks good on calm data isn&apos;t much use. These
@@ -958,10 +967,9 @@ function Chart({ data, value }) {
<ChartPlaceholder height={200} /> <ChartPlaceholder height={200} />
<Divider /> </Section>
{/* Just a line */} <Section id="just-a-line" title="Just a line">
<SectionHeading id="just-a-line">Just a line</SectionHeading>
<Paragraph> <Paragraph>
Liveline can do a lot. Momentum arrows, particles, orderbooks, Liveline can do a lot. Momentum arrows, particles, orderbooks,
@@ -971,18 +979,17 @@ function Chart({ data, value }) {
<ChartPlaceholder height={180} /> <ChartPlaceholder height={180} />
<Divider /> </Section>
{/* Acknowledgements */} <Section id="acknowledgements" title="Acknowledgements">
<SectionHeading id="acknowledgements">
Acknowledgements
</SectionHeading>
<Paragraph> <Paragraph>
Built with React and HTML Canvas. Inspired by TradingView, Robinhood, Built with React and HTML Canvas. Inspired by TradingView, Robinhood,
and Polymarket chart aesthetics. The interpolation approach is and Polymarket chart aesthetics. The interpolation approach is
borrowed from game development &mdash; lerp everything, snap nothing. borrowed from game development &mdash; lerp everything, snap nothing.
</Paragraph> </Paragraph>
</Section>
</article> </article>
</div> </div>
</div> </div>