Skip to content

Commit 13e48a8

Browse files
committed
feat(devtools): update SEO tab styles and improve health score visualization
This commit enhances the SEO tab by updating styles for the health score indicators, including a new design for the health track and fill elements. It refactors the health score rendering logic to utilize a more consistent approach across components, improving accessibility with ARIA attributes. Additionally, it introduces a sorting function for links in the report, ensuring a clearer display order based on link types. These changes aim to provide a more cohesive and visually appealing user experience in the SEO analysis features.
1 parent 10e295e commit 13e48a8

4 files changed

Lines changed: 88 additions & 60 deletions

File tree

packages/devtools/src/styles/use-styles.ts

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -579,24 +579,41 @@ const stylesFactory = (theme: DevtoolsStore['settings']['theme']) => {
579579
`,
580580
seoHealthTrack: css`
581581
width: 100%;
582-
height: 5px;
583-
background: ${t(colors.gray[200], colors.gray[800])};
582+
height: 10px;
584583
border-radius: 999px;
584+
background: ${t(colors.gray[100], colors.gray[800])};
585+
border: 1px solid ${t(colors.gray[200], colors.gray[700])};
585586
overflow: hidden;
587+
box-shadow: inset 0 1px 2px ${t('rgba(15, 23, 42, 0.06)', 'rgba(0, 0, 0, 0.35)')};
586588
`,
587-
seoHealthBarSvg: css`
588-
width: 100%;
589-
height: 5px;
590-
display: block;
591-
`,
592-
seoHealthRectGood: css`
593-
fill: #16a34a;
594-
`,
595-
seoHealthRectFair: css`
596-
fill: #d97706;
597-
`,
598-
seoHealthRectPoor: css`
599-
fill: #dc2626;
589+
seoHealthFill: css`
590+
height: 100%;
591+
min-width: 0;
592+
max-width: 100%;
593+
border-radius: 999px;
594+
transition: width 0.45s cubic-bezier(0.33, 1, 0.68, 1);
595+
box-shadow: 0 1px 2px ${t('rgba(15, 23, 42, 0.12)', 'rgba(0, 0, 0, 0.25)')};
596+
`,
597+
seoHealthFillGood: css`
598+
background: linear-gradient(
599+
90deg,
600+
${t(colors.green[700], '#15803d')} 0%,
601+
${t(colors.green[500], '#22c55e')} 100%
602+
);
603+
`,
604+
seoHealthFillFair: css`
605+
background: linear-gradient(
606+
90deg,
607+
${t(colors.yellow[700], '#b45309')} 0%,
608+
${t(colors.yellow[500], '#eab308')} 100%
609+
);
610+
`,
611+
seoHealthFillPoor: css`
612+
background: linear-gradient(
613+
90deg,
614+
${t(colors.red[700], '#b91c1c')} 0%,
615+
${t(colors.red[500], '#ef4444')} 100%
616+
);
600617
`,
601618
seoHealthCountsRow: css`
602619
display: flex;

packages/devtools/src/tabs/seo-tab/json-ld-preview.tsx

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,15 @@ export function JsonLdPreviewSection() {
400400
? s.seoHealthScoreFair
401401
: s.seoHealthScorePoor
402402
}
403-
const healthRectClass = () => {
403+
const healthFillClass = () => {
404404
const tier = seoHealthTier(score)
405-
return tier === 'good'
406-
? s.seoHealthRectGood
407-
: tier === 'fair'
408-
? s.seoHealthRectFair
409-
: s.seoHealthRectPoor
405+
const tierFill =
406+
tier === 'good'
407+
? s.seoHealthFillGood
408+
: tier === 'fair'
409+
? s.seoHealthFillFair
410+
: s.seoHealthFillPoor
411+
return `${s.seoHealthFill} ${tierFill}`
410412
}
411413
const errorCount = entries.reduce(
412414
(total, entry) =>
@@ -439,22 +441,18 @@ export function JsonLdPreviewSection() {
439441
<span class={s.seoJsonLdHealthTitle}>JSON-LD Health</span>
440442
<span class={healthScoreClass()}>{score}%</span>
441443
</div>
442-
<div class={s.seoHealthTrack}>
443-
<svg
444-
class={s.seoHealthBarSvg}
445-
viewBox="0 0 100 5"
446-
preserveAspectRatio="none"
447-
aria-hidden="true"
448-
>
449-
<rect
450-
class={healthRectClass()}
451-
x="0"
452-
y="0"
453-
width={Math.min(100, Math.max(0, score))}
454-
height="5"
455-
rx="2.5"
456-
/>
457-
</svg>
444+
<div
445+
class={s.seoHealthTrack}
446+
role="progressbar"
447+
aria-valuemin={0}
448+
aria-valuemax={100}
449+
aria-valuenow={Math.round(score)}
450+
aria-label={`JSON-LD health ${Math.round(score)} percent`}
451+
>
452+
<div
453+
class={healthFillClass()}
454+
style={{ width: `${Math.min(100, Math.max(0, score))}%` }}
455+
/>
458456
</div>
459457
<div class={s.seoHealthCountsRow}>
460458
<span class={s.seoHealthCountError}>

packages/devtools/src/tabs/seo-tab/links-preview.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ export function analyzeLinks(): Array<LinkRow> {
115115
.map(classifyLink)
116116
}
117117

118+
/** Display order in the links report: internal, external, non-web, then invalid. */
119+
const LINK_KIND_DISPLAY_ORDER: Record<LinkKind, number> = {
120+
internal: 0,
121+
external: 1,
122+
'non-web': 2,
123+
invalid: 3,
124+
}
125+
126+
export function sortLinksForDisplay(links: Array<LinkRow>): Array<LinkRow> {
127+
return [...links].sort(
128+
(a, b) => LINK_KIND_DISPLAY_ORDER[a.kind] - LINK_KIND_DISPLAY_ORDER[b.kind],
129+
)
130+
}
131+
118132
/**
119133
* Link-level issues (capped) and totals for the SEO overview.
120134
*/
@@ -154,6 +168,7 @@ function linkKindBadgeClass(
154168
export function LinksPreviewSection() {
155169
const styles = useStyles()
156170
const links = analyzeLinks()
171+
const linksForReport = sortLinksForDisplay(links)
157172
const issueCount = links.reduce((count, row) => count + row.issues.length, 0)
158173

159174
const counts = links.reduce(
@@ -216,7 +231,7 @@ export function LinksPreviewSection() {
216231
<div class={s.serpPreviewBlock}>
217232
<div class={s.serpPreviewLabel}>Links report</div>
218233
<ul class={s.seoLinksReportList}>
219-
<For each={links}>
234+
<For each={linksForReport}>
220235
{(row) => (
221236
<li class={s.seoLinksReportItem}>
222237
<div class={s.seoLinksReportTopRow}>

packages/devtools/src/tabs/seo-tab/seo-overview.tsx

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,16 @@ export function SeoOverviewSection(props: { goTo: (view: SeoDetailView) => void
9595
: s.seoHealthScorePoor
9696
}
9797

98-
const healthRectClass = (score: number) => {
98+
const healthFillClass = (score: number) => {
9999
const s = styles()
100100
const tier = seoHealthTier(score)
101-
return tier === 'good'
102-
? s.seoHealthRectGood
103-
: tier === 'fair'
104-
? s.seoHealthRectFair
105-
: s.seoHealthRectPoor
101+
const tierFill =
102+
tier === 'good'
103+
? s.seoHealthFillGood
104+
: tier === 'fair'
105+
? s.seoHealthFillFair
106+
: s.seoHealthFillPoor
107+
return `${s.seoHealthFill} ${tierFill}`
106108
}
107109

108110
const sectionIconClass = (sev: SeoSeverity | null) => {
@@ -148,22 +150,18 @@ export function SeoOverviewSection(props: { goTo: (view: SeoDetailView) => void
148150
{bundle().health.score}%
149151
</span>
150152
</div>
151-
<div class={styles().seoHealthTrack}>
152-
<svg
153-
class={styles().seoHealthBarSvg}
154-
viewBox="0 0 100 5"
155-
preserveAspectRatio="none"
156-
aria-hidden="true"
157-
>
158-
<rect
159-
class={healthRectClass(bundle().health.score)}
160-
x="0"
161-
y="0"
162-
width={Math.min(100, Math.max(0, bundle().health.score))}
163-
height="5"
164-
rx="2.5"
165-
/>
166-
</svg>
153+
<div
154+
class={styles().seoHealthTrack}
155+
role="progressbar"
156+
aria-valuemin={0}
157+
aria-valuemax={100}
158+
aria-valuenow={Math.round(bundle().health.score)}
159+
aria-label={`Overall SEO health ${Math.round(bundle().health.score)} percent`}
160+
>
161+
<div
162+
class={healthFillClass(bundle().health.score)}
163+
style={{ width: `${Math.min(100, Math.max(0, bundle().health.score))}%` }}
164+
/>
167165
</div>
168166
<div class={styles().seoHealthCountsRow}>
169167
<span class={styles().seoHealthCountError}>

0 commit comments

Comments
 (0)