Restyle footer, allow >4 link groups (#2761)

This commit is contained in:
Zeno Kapitein
2025-01-21 14:50:33 +01:00
committed by GitHub
parent b46cff70c0
commit e5dc05e994
5 changed files with 174 additions and 144 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': patch
---
Update footer styling and allow for more than 4 footer groups
+101 -108
View File
@@ -2,6 +2,7 @@ import { CustomizationSettings, SiteCustomizationSettings, Space } from '@gitboo
import React from 'react';
import { Image } from '@/components/utils';
import { partition } from '@/lib/arrays';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
@@ -9,6 +10,8 @@ import { FooterLinksGroup } from './FooterLinksGroup';
import { CONTAINER_STYLE } from '../layout';
import { ThemeToggler } from '../ThemeToggler';
const FOOTER_COLUMNS = 4;
export function Footer(props: {
space: Space;
context: ContentRefContext;
@@ -17,125 +20,115 @@ export function Footer(props: {
const { context, customization } = props;
return (
<div
className={tcls(
'border-t',
'border-dark/2',
'bg-light-2',
'dark:border-light/2',
'dark:bg-dark-2',
)}
>
<div className={tcls('scroll-nojump')}>
<footer className="border-t border-dark/2 dark:border-light/2 scroll-nojump">
<div
className={tcls(
CONTAINER_STYLE,
'py-8',
'gap-12',
'flex',
'flex-wrap',
'items-start',
)}
>
{/* Footer Logo */}
<div className="shrink sm:basis-72 page-no-toc:lg:basis-56 mr-auto order-1 empty:hidden empty:lg:block page-no-toc:empty:lg:hidden page-no-toc:empty:xl:block">
{customization.footer.logo ? (
<Image
alt="Logo"
sources={{
light: {
src: customization.footer.logo.light,
},
dark: customization.footer.logo.dark
? {
src: customization.footer.logo.dark,
}
: null,
}}
priority="lazy"
style={[
'w-auto',
'max-w-40',
'lg:max-w-64',
'max-h-12',
'object-contain',
'object-left',
'rounded',
'straight-corners:rounded-sm',
]}
sizes={[
{
width: 320,
},
]}
/>
) : null}
</div>
{/* Mode Switcher */}
<div
className={tcls(
'flex',
'flex-col',
CONTAINER_STYLE,
'py-6',
'gap-6',
'md:flex-row',
'md:gap-10',
//override CONTAINER_STYLE px
'px-0',
'sm:px-0',
'ml-auto',
'order-2 lg:order-4',
customization.footer.groups?.length < 3 &&
customization.footer.logo == undefined &&
'sm:order-4',
'xl:basis-56',
)}
>
{' '}
{customization.footer.logo ? (
<div
className={tcls(
'pt-8',
'border-t',
'border-dark/2',
'dark:border-light/1',
'px-4',
'sm:px-6',
'md:px-0',
'md:pr-10',
'md:border-r',
'md:border-t-0',
'md:pt-0',
)}
>
<Image
alt="Logo"
sources={{
light: {
src: customization.footer.logo.light,
},
dark: customization.footer.logo.dark
? {
src: customization.footer.logo.dark,
}
: null,
}}
priority="lazy"
style={['h-auto', 'w-full', 'max-w-12', 'object-contain']}
sizes={[
{
width: 320,
},
]}
/>
</div>
) : null}
<div
className={tcls(
'flex-1',
'flex',
'flex-col',
'gap-6',
'px-4',
'sm:px-6',
'md:px-0',
)}
>
{customization.footer.logo || customization.footer.groups?.length > 0 ? (
<div
className={tcls(
'flex',
'flex-col',
'gap-10',
'sm:gap-20',
'sm:flex-row',
'items-start',
'flex-wrap',
)}
>
{customization.footer.groups.map((group, index) => (
<FooterLinksGroup key={index} group={group} context={context} />
))}
</div>
) : null}
{customization.footer.copyright ? (
<p className={tcls('text-xs', 'text-dark/7', 'dark:text-light/6')}>
{customization.footer.copyright}
</p>
) : null}
</div>
{customization.themes.toggeable ? (
<div
className={tcls(
'flex',
'flex-col',
'items-start',
'order-[-1]',
'px-4',
'sm:px-6',
'md:px-0',
'md:order-1',
)}
>
<div className="flex items-center justify-end">
<React.Suspense fallback={null}>
<ThemeToggler />
</React.Suspense>
</div>
) : null}
</div>
{/* Navigation Groups (split into equal columns) */}
{customization.footer.groups?.length > 0 ? (
<div
className={tcls(
'flex flex-col sm:flex-row mx-auto grow lg:max-w-3xl gap-10 sm:gap-6 order-3',
'w-full lg:w-auto',
customization.footer.groups?.length < 3 &&
customization.footer.logo == undefined &&
'sm:w-auto',
)}
>
{partition(customization.footer.groups, FOOTER_COLUMNS).map(
(column, columnIndex) => (
<div key={columnIndex} className="flex w-full flex-col gap-10">
{column.map((group, groupIndex) => (
<FooterLinksGroup
key={groupIndex}
group={group}
context={context}
/>
))}
</div>
),
)}
</div>
) : null}
{/* Legal */}
<div
className={tcls(
'mx-auto w-full grow text-xs text-dark/7 dark:text-light/6 items-center text-center order-4 flex flex-col gap-2 empty:hidden',
customization.footer.groups.length == 0 &&
'order-2 lg:flex-1 lg:w-auto lg:items-start lg:max-w-3xl self-center lg:text-start',
customization.footer.groups.length == 0 &&
customization.footer.logo == undefined &&
'sm:w-auto sm:flex-1 sm:items-start sm:max-w-3xl sm:text-start',
)}
>
{customization.footer.copyright ? (
<p>{customization.footer.copyright}</p>
) : null}
</div>
</div>
</div>
</footer>
);
}
@@ -16,12 +16,18 @@ export function FooterLinksGroup(props: {
const { group, context } = props;
return (
<div className={tcls('flex', 'flex-col', 'gap-3')}>
<p className={tcls('text-base', 'font-semibold')}>{group.title}</p>
{group.links.map((link, index) => {
return <FooterLink key={index} link={link} context={context} />;
})}
</div>
<nav className="text-sm flex flex-col gap-4">
<h4 className="font-semibold">{group.title}</h4>
<ul className="flex flex-col items-start gap-4">
{group.links.map((link, index) => {
return (
<li key={index}>
<FooterLink link={link} context={context} />
</li>
);
})}
</ul>
</nav>
);
}
@@ -37,12 +43,15 @@ async function FooterLink(props: { link: CustomizationContentLink; context: Cont
<Link
href={resolved.href}
className={tcls(
'text-sm',
'font-normal',
'text-dark/8',
'hover:text-dark/9',
'hover:text-primary',
'dark:text-light/8',
'dark:hover:text-light/9',
'dark:hover:text-light',
'contrast-more:underline',
'contrast-more:text-dark',
'contrast-more:dark:text-light',
'underline-offset-2',
)}
insights={{
target: link.to,
@@ -27,18 +27,7 @@ export function ThemeToggler(props: {}) {
};
return (
<div
role="radiogroup"
className={tcls(
'flex',
'flex-row',
'rounded-full',
'straight-corners:rounded-sm',
'border',
'border-dark/3',
'dark:border-light/2',
)}
>
<div role="radiogroup" className="flex flex-row gap-2">
<ThemeButton
active={mounted && theme === 'light'}
icon="sun-bright"
@@ -74,25 +63,37 @@ function ThemeButton(props: {
role="radio"
onClick={onClick}
aria-label={title}
title={title}
aria-checked={active}
className={tcls(
'p-1',
'm-1',
'group',
'rounded-full',
'straight-corners:rounded-sm',
active ? ['bg-primary-600/4', 'dark:bg-primary-400/2'] : null,
'text-dark',
'dark:text-light/7',
'p-2',
'rounded',
'straight-corners:rounded-none',
'transition-all',
'text-dark/8',
'dark:text-light/8',
'contrast-more:text-dark',
'dark:contrast-more:text-light',
'hover:bg-dark/1',
'dark:hover:bg-light/1',
'contrast-more:hover:ring-2',
'contrast-more:focus:ring-2',
'ring-dark',
'dark:ring-light',
active && [
'bg-tint/2',
'hover:bg-tint/2',
'text-tint-600',
'dark:text-tint-400',
'contrast-more:text-tint-600',
'contrast-more:dark:text-tint-400',
'contrast-more:ring-1',
'ring-tint-600',
'dark:ring-tint-400',
],
)}
>
<Icon
icon={icon}
className={tcls(
'size-4',
active ? ['text-primary-600', 'dark:text-primary-400'] : null,
)}
/>
<Icon icon={icon} className={tcls('size-4')} />
</button>
);
}
+22
View File
@@ -0,0 +1,22 @@
/**
* Partition an array into equal (+ rest items) parts.
* Adapted from https://stackoverflow.com/a/68917937
*/
export function partition(
/**
* Array to split up
*/
array: any[],
/**
* Amount of parts you want to split into
*/
parts: number,
) {
let rest = array.length % parts;
let size = Math.floor(array.length / parts);
let j = 0;
return Array.from({ length: Math.min(array.length, parts) }, (_, i) =>
array.slice(j, (j += size + (i < rest ? 1 : 0))),
);
}