fix(stats): refine hero layout for responsive Figma match

This commit is contained in:
Adam 2026-05-28 12:43:04 -05:00
parent fff7781fa8
commit 3ce9b4be0d
No known key found for this signature in database
GPG key ID: 9CB48779AF150E75
2 changed files with 46 additions and 19 deletions

View file

@ -89,7 +89,7 @@
[data-page="stats"] [data-component="container"] {
box-sizing: border-box;
width: 100%;
max-width: 80rem;
max-width: calc(80rem + 2px);
margin: 0 auto;
border-left: 1px solid var(--stats-line);
border-right: 1px solid var(--stats-line);
@ -461,6 +461,7 @@
}
[data-page="stats"] [data-section="hero"] h1 {
order: 1;
color: var(--stats-text);
font-size: 64px;
font-weight: 500;
@ -469,20 +470,30 @@
}
[data-page="stats"] [data-slot="hero-copy"] {
order: 3;
color: var(--stats-hero-muted);
font-size: 16px;
font-weight: 400;
line-height: 1.5;
}
[data-page="stats"] [data-slot="hero-copy-break"] {
display: none;
}
[data-page="stats"] [data-slot="hero-pattern"] {
order: 2;
flex: 0 0 auto;
width: 100%;
height: 16px;
overflow: hidden;
background-image: radial-gradient(circle, var(--stats-hero-pattern) 1px, transparent 1.1px);
background-position: 0 0;
background-size: 6px 6px;
background: var(--stats-hero-pattern);
mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E");
mask-repeat: repeat;
mask-size: 6px 6px;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0H2V2H0V0Z' fill='black'/%3E%3C/svg%3E");
-webkit-mask-repeat: repeat;
-webkit-mask-size: 6px 6px;
}
[data-page="stats"] [data-slot="hero-meta"] {
@ -496,7 +507,7 @@
background: var(--stats-layer-2);
color: var(--stats-hero-muted);
font-size: 13px;
font-weight: 600;
font-weight: 500;
line-height: 1.1;
overflow: hidden;
white-space: nowrap;
@ -514,10 +525,10 @@
text-overflow: ellipsis;
}
@media (min-width: 58rem) {
@media (min-width: 48rem) {
[data-page="stats"] [data-section="hero"] {
gap: 16px;
padding: 128px 40px 24px;
padding: 128px 32px 32px;
}
[data-page="stats"] [data-slot="hero-canvas"] {
@ -553,11 +564,21 @@
right: 0;
bottom: 0;
z-index: 1;
width: min(36rem, 100%);
width: min(563px, 100%);
padding: 12px 0 0 16px;
background: var(--stats-bg);
text-align: right;
}
[data-page="stats"] [data-slot="hero-copy-break"] {
display: block;
}
}
@media (min-width: 75rem) {
[data-page="stats"] [data-section="hero"] {
padding: 128px 40px 24px;
}
}
@media (min-width: 90rem) {
@ -1494,7 +1515,7 @@
}
}
@media (max-width: 74rem) {
@media (max-width: 80rem) {
[data-page="stats"] [data-component="container"] {
border: 0;
}
@ -1540,14 +1561,12 @@
--stats-section-padding: 4rem;
}
[data-page="stats"] [data-section="hero"],
[data-page="stats"] [data-section="chart"],
[data-page="stats"] [data-section="newsletter"] {
padding-left: 24px;
padding-right: 24px;
}
[data-page="stats"] [data-section="hero"],
[data-page="stats"] [data-section="newsletter"] {
grid-template-columns: 1fr;
}

View file

@ -18,7 +18,7 @@ import {
import { runtime } from "@opencode-ai/stats-core/runtime"
import { createAsync, query } from "@solidjs/router"
import { scaleBand, scaleLinear } from "d3-scale"
import { createEffect, createMemo, createSignal, For, onCleanup, Show, type JSX } from "solid-js"
import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show, type JSX } from "solid-js"
import { getRequestEvent } from "solid-js/web"
const products = ["All Users", "Zen", "Go", "Enterprise"] as const
@ -98,21 +98,29 @@ export default function StatsHome() {
}
function Hero(props: { updatedAt: string | null }) {
const [timeZone, setTimeZone] = createSignal("UTC")
onMount(() => setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"))
return (
<section data-section="hero">
<p data-slot="hero-meta">
<svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16">
<rect x="3" y="3" width="10" height="10" fill="currentColor" />
<rect x="7" y="6.5" width="2" height="4.5" fill="var(--stats-layer-2)" />
<rect x="7" y="5" width="2" height="1" fill="var(--stats-layer-2)" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M13 13H3V3H13V13ZM6.46777 6.81641V7.81641H7.5791V11.3721H8.5791V6.81641H6.46777ZM7.30078 4.62891V5.62891H8.85645V4.62891H7.30078Z"
fill="currentColor"
/>
</svg>
<span>{props.updatedAt ? `Updated ${formatUpdatedAt(props.updatedAt)}` : "No rows yet"}</span>
<span>{props.updatedAt ? `Updated ${formatUpdatedAt(props.updatedAt, timeZone())}` : "No rows yet"}</span>
</p>
<div data-slot="hero-canvas">
<div data-slot="hero-pattern" aria-hidden="true" />
<h1>Model Stats</h1>
<p data-slot="hero-copy">
See which models are winning real usage, how the mix shifts over time, and where momentum is moving each week.
See which models are winning real usage, how the mix{" "}
<br data-slot="hero-copy-break" />
shifts over time, and where momentum is moving each week.
</p>
</div>
</section>
@ -154,7 +162,7 @@ function EmptyState(props: { title: string; description: string }) {
)
}
function formatUpdatedAt(value: string) {
function formatUpdatedAt(value: string, timeZone: string) {
const date = new Date(value)
if (Number.isNaN(date.getTime())) return "just now"
return new Intl.DateTimeFormat("en", {
@ -162,7 +170,7 @@ function formatUpdatedAt(value: string) {
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "UTC",
timeZone,
timeZoneName: "short",
}).format(date)
}