website: plugins section spacing with margins, not flex gaps

PluginSection takes its header as a prop and puts the content 32px
below it with a margin. Sections on the index are 56px apart via
space-y. The browse page h1 sits 32px above the sort tabs, matching
the index headings.
This commit is contained in:
Mohamed Boudra
2026-10-06 22:23:50 +02:00
parent 66b2567be3
commit 72c180aaf7
3 changed files with 66 additions and 41 deletions
+2 -3
View File
@@ -60,7 +60,7 @@ export function BrowsePage({
<CategoryNav plugins={matches} query={query} />
</aside>
<div className="min-w-0 flex-1">
<div className="mb-6 flex items-baseline justify-between gap-4">
<div className="mb-8 flex items-baseline justify-between gap-4">
<h1 className="text-3xl font-medium tracking-tight">
{query.q ? `Results for “${query.q}”${category ? ` in ${title}` : ""}` : title}
<span className="ml-3 align-middle text-sm font-normal tabular-nums text-extra-muted-foreground">
@@ -73,8 +73,7 @@ export function BrowsePage({
</a>
)}
</div>
<PluginSection>
<SortTabs query={query} />
<PluginSection header={<SortTabs query={query} />}>
{results.length === 0 && query.q && (
<div className="rounded-xl border border-white/10 bg-white/[0.03] px-4 py-10 text-center">
<p className="text-sm text-muted-foreground">No plugins match.</p>
+7 -4
View File
@@ -1,20 +1,23 @@
import type { ReactNode } from "react";
/** A block of a plugins page: its header, then its content one fixed gap below. */
/** A block of a plugins page: its header, then its content 32px below. */
export function PluginSection({
id,
labelledBy,
className = "",
className,
header,
children,
}: {
id?: string;
labelledBy?: string;
className?: string;
header: ReactNode;
children: ReactNode;
}) {
return (
<section id={id} aria-labelledby={labelledBy} className={`flex flex-col gap-8 ${className}`}>
{children}
<section id={id} aria-labelledby={labelledBy} className={className}>
{header}
<div className="mt-8">{children}</div>
</section>
);
}
+57 -34
View File
@@ -128,19 +128,31 @@ function PluginsPage() {
</div>
</div>
<div className="mt-10 flex flex-col gap-14">
<div className="mt-10 space-y-14">
{featured.length > 0 && (
<PluginSection labelledBy="featured">
<PluginSectionHeader>
<div>
<PluginSectionTitle id="featured">Featured</PluginSectionTitle>
<p className="mt-1 text-sm text-muted-foreground">A selection of hand picked plugins</p>
</div>
<a href={browseHref({ sort: "installs", window: DEFAULT_WINDOW })} className={SEE_ALL_CLASS}>
See all plugins
<ChevronRight className="h-3.5 w-3.5" />
</a>
</PluginSectionHeader>
<PluginSection
labelledBy="featured"
header={
<PluginSectionHeader>
<div>
<PluginSectionTitle id="featured">Featured</PluginSectionTitle>
<p className="mt-1 text-sm text-muted-foreground">
A selection of hand picked plugins
</p>
</div>
<a
href={browseHref({
sort: "installs",
window: DEFAULT_WINDOW,
})}
className={SEE_ALL_CLASS}
>
See all plugins
<ChevronRight className="h-3.5 w-3.5" />
</a>
</PluginSectionHeader>
}
>
<div className={CARD_ROW_CLASS}>
{featured.map((plugin) => (
<NewPluginCard key={plugin.id} plugin={plugin} added={addedAgo(plugin, now)} />
@@ -149,14 +161,21 @@ function PluginsPage() {
</PluginSection>
)}
<PluginSection labelledBy="whats-new">
<PluginSectionHeader>
<PluginSectionTitle id="whats-new">What’s new</PluginSectionTitle>
<a href={browseHref({ sort: "new", window: DEFAULT_WINDOW })} className={SEE_ALL_CLASS}>
See all
<ChevronRight className="h-3.5 w-3.5" />
</a>
</PluginSectionHeader>
<PluginSection
labelledBy="whats-new"
header={
<PluginSectionHeader>
<PluginSectionTitle id="whats-new">What’s new</PluginSectionTitle>
<a
href={browseHref({ sort: "new", window: DEFAULT_WINDOW })}
className={SEE_ALL_CLASS}
>
See all
<ChevronRight className="h-3.5 w-3.5" />
</a>
</PluginSectionHeader>
}
>
<div className={CARD_ROW_CLASS}>
{newest.map((plugin) => (
<NewPluginCard key={plugin.id} plugin={plugin} added={addedAgo(plugin, now)} />
@@ -164,8 +183,10 @@ function PluginsPage() {
</div>
</PluginSection>
<PluginSection labelledBy="categories">
<PluginSectionTitle id="categories">Categories</PluginSectionTitle>
<PluginSection
labelledBy="categories"
header={<PluginSectionTitle id="categories">Categories</PluginSectionTitle>}
>
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
{CATEGORIES.map((category) => {
const Icon = CATEGORY_ICONS[category.slug];
@@ -192,19 +213,21 @@ function PluginsPage() {
id="most-installed"
labelledBy="most-installed-title"
className="scroll-mt-8"
header={
<PluginSectionHeader>
<PluginSectionTitle id="most-installed-title">
<a
href={browseHref({ sort: "installs", window })}
className="group inline-flex items-center gap-1"
>
Most installed
<ChevronRight className="h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" />
</a>
</PluginSectionTitle>
<WindowSwitch current={window} hrefs={windowHrefs} />
</PluginSectionHeader>
}
>
<PluginSectionHeader>
<PluginSectionTitle id="most-installed-title">
<a
href={browseHref({ sort: "installs", window })}
className="group inline-flex items-center gap-1"
>
Most installed
<ChevronRight className="h-4 w-4 text-muted-foreground transition-colors group-hover:text-foreground" />
</a>
</PluginSectionTitle>
<WindowSwitch current={window} hrefs={windowHrefs} />
</PluginSectionHeader>
<div className="-mx-4 grid gap-x-8 md:grid-cols-2">
{top.map((plugin, index) => (
<PluginRankRow