mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-22 10:33:22 +00:00
Styling PR 1 (#16)
* add maxwidth limit for very large screens
* testing animation on hints
* page aside style on darkmode
* fix icon TOC
* Make scrollbars stable when overflowing
* fix scrollbar on search
* search compact header
* search style
* test without bg toc
* Commit
* setup before mobile TOC
* test if style{} clashes
* fix TOC hover
* add prevent default on search shortcut
* add deps
* Add header mobile menu setup
* work on fallback logo
* style search modal position
* style hamburger button
* padding
* add rudimental toggle
* comment
* gap value responsive
* remove slate
* fix z index
* undo test
* Install and format
---------
Co-authored-by: Samy Pessé <samypesse@gmail.com>
This commit is contained in:
+4
-3
@@ -14,8 +14,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@geist-ui/icons": "^1.0.2",
|
||||
"@gitbook/api": "^0.15.0",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@gitbook/api": "^0.17.0",
|
||||
"@readme/openapi-parser": "^2.5.0",
|
||||
"@upstash/redis": "^1.25.1",
|
||||
"ajv": "^8.12.0",
|
||||
@@ -23,14 +23,15 @@
|
||||
"bun-types": "^1.0.7",
|
||||
"jsontoxml": "^1.0.1",
|
||||
"katex": "^0.16.9",
|
||||
"next": "^14.0.4-canary.25",
|
||||
"next": "^14.0.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-hotkeys-hook": "^4.4.1",
|
||||
"recoil": "^0.7.7",
|
||||
"server-only": "^0.0.1",
|
||||
"shiki": "^0.14.5",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwind-shades": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/next-on-pages": "^1.7.3",
|
||||
|
||||
@@ -31,10 +31,63 @@
|
||||
p {
|
||||
@apply text-dark/9 dark:text-light/8;
|
||||
}
|
||||
|
||||
/* Light mode */
|
||||
::-webkit-scrollbar {
|
||||
@apply bg-transparent;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-dark/4;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-dark/5;
|
||||
}
|
||||
|
||||
/* Dark mode */
|
||||
.dark ::-webkit-scrollbar {
|
||||
@apply bg-light/1;
|
||||
}
|
||||
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
@apply bg-light/3;
|
||||
}
|
||||
|
||||
.dark ::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-light/4;
|
||||
}
|
||||
|
||||
/* Inverted theme light mode */
|
||||
.inverted-theme-scroll::-webkit-scrollbar {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
.inverted-theme-scroll::-webkit-scrollbar-thumb {
|
||||
@apply bg-dark/5;
|
||||
}
|
||||
|
||||
.inverted-theme-scroll::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-dark/6;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.linear-mask-gradient {
|
||||
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 96px, rgba(0, 0, 0, 0));
|
||||
}
|
||||
.grid-area-1-1 {
|
||||
grid-area: 1/1;
|
||||
}
|
||||
.gutter-stable {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
.textwrap-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
.textwrap-pretty {
|
||||
text-wrap: pretty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ export async function File(props: BlockProps<DocumentBlockFile>) {
|
||||
'hover:border-slate-300',
|
||||
'px-5',
|
||||
'py-2',
|
||||
'text-slate-500',
|
||||
'hover:text-slate-700',
|
||||
/* 'text-slate-500', */
|
||||
/* 'hover:text-slate-700', */
|
||||
style,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -3,6 +3,7 @@ import IconAlertTriangle from '@geist-ui/icons/alertTriangle';
|
||||
import IconCheckInCircle from '@geist-ui/icons/checkInCircle';
|
||||
import IconInfo from '@geist-ui/icons/info';
|
||||
import { DocumentBlockHint } from '@gitbook/api';
|
||||
import React from 'react';
|
||||
|
||||
import { ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
@@ -14,6 +15,7 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
|
||||
const { block, style, ancestorBlocks, ...contextProps } = props;
|
||||
const hintStyle = HINT_STYLES[block.data.style];
|
||||
const firstLine = getBlockTextStyle(block.nodes[0]);
|
||||
const RenderIcon = HINT_STYLES[block.data.style].icon;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -24,7 +26,11 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
|
||||
'py-4',
|
||||
'transition-colors',
|
||||
'rounded',
|
||||
'border-l-4',
|
||||
'border-b-[1px]',
|
||||
'dark:border-t-[1px]',
|
||||
'dark:border-b-0',
|
||||
|
||||
/* 'shadow-1xs', */
|
||||
hintStyle.style,
|
||||
style,
|
||||
)}
|
||||
@@ -40,7 +46,9 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
|
||||
hintStyle.iconStyle,
|
||||
)}
|
||||
>
|
||||
<hintStyle.icon className={tcls('w-5', 'h-5')} />
|
||||
{/* <hintStyle.icon className={tcls('w-5', 'h-5')} /> */}
|
||||
|
||||
<RenderIcon />
|
||||
</div>
|
||||
<Blocks
|
||||
{...contextProps}
|
||||
@@ -52,31 +60,100 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
|
||||
);
|
||||
}
|
||||
|
||||
const SuccessIcon = () => (
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'w-[17px]',
|
||||
'h-[17px]',
|
||||
'bg-white',
|
||||
'rounded-full',
|
||||
'justify-center',
|
||||
'items-center',
|
||||
'dark:bg-light/3',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={tcls(
|
||||
'grid',
|
||||
'justify-items-center',
|
||||
'items-center',
|
||||
'w-[5px]',
|
||||
'h-[5px]',
|
||||
'top-[-0.5px]',
|
||||
'relative',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={tcls(
|
||||
'animate-[pingAlt_8s_ease_infinite_forwards]',
|
||||
'bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-dark/2 from-0% via-dark/2 via-55% to-dark to-100%',
|
||||
'grid-area-1-1',
|
||||
'w-[5px]',
|
||||
'h-[5px]',
|
||||
'rounded',
|
||||
'dark:from-light/2 dark:via-light/2 dark:to-light/8',
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
className={tcls(
|
||||
'animate-[pulse_8s_ease_infinite]',
|
||||
'grid-area-1-1',
|
||||
'w-[3px]',
|
||||
'h-[3px]',
|
||||
'bg-dark/7',
|
||||
'rounded',
|
||||
'dark:bg-light/8',
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
className={tcls(
|
||||
'animate-[pulse_8s_ease_infinite]',
|
||||
'w-[3px]',
|
||||
'h-[7px]',
|
||||
'bg-dark/7',
|
||||
'rounded',
|
||||
'dark:bg-light/8',
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
const WarningIcon = () => SuccessIcon();
|
||||
const DangerIcon = () => SuccessIcon();
|
||||
const InfoIcon = () => SuccessIcon();
|
||||
|
||||
const HINT_STYLES: {
|
||||
[style in DocumentBlockHint['data']['style']]: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
icon: React.ComponentType;
|
||||
iconStyle: ClassValue;
|
||||
style: ClassValue;
|
||||
};
|
||||
} = {
|
||||
info: {
|
||||
icon: IconInfo,
|
||||
icon: SuccessIcon,
|
||||
iconStyle: ['text-current'],
|
||||
style: ['border-dark/2', 'bg-dark/2', 'dark:bg-light/1', 'dark:border-light/3'],
|
||||
style: ['border-dark/1', 'bg-dark/2', 'dark:bg-light/1', 'dark:border-light/1'],
|
||||
},
|
||||
warning: {
|
||||
icon: IconAlertCircle,
|
||||
icon: WarningIcon,
|
||||
iconStyle: ['text-yellow, dark:text-yellow/1, dark:text-yellow/8'],
|
||||
style: ['border-yellow/4', 'bg-yellow', 'dark:bg-yellow/1'],
|
||||
style: ['border-yellow/6', 'bg-yellow/6', 'dark:bg-yellow/1', 'dark:border-yellow/2'],
|
||||
},
|
||||
danger: {
|
||||
icon: IconAlertTriangle,
|
||||
icon: DangerIcon,
|
||||
iconStyle: ['text-pomegranate'],
|
||||
style: ['border-pomegranate/4', 'bg-pomegranate/2', 'dark:bg-pomegranate/1'],
|
||||
style: [
|
||||
'border-pomegranate/2',
|
||||
'bg-pomegranate/2',
|
||||
'dark:bg-pomegranate/1',
|
||||
'dark:border-pomegranate/3',
|
||||
],
|
||||
},
|
||||
success: {
|
||||
icon: IconCheckInCircle,
|
||||
icon: InfoIcon,
|
||||
iconStyle: ['text-teal'],
|
||||
style: ['border-teal/4', 'bg-teal/2', 'dark:bg-teal/2'],
|
||||
style: ['border-teal/2', 'bg-teal/2', 'dark:bg-teal/2'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export function Images(props: BlockProps<DocumentBlockImages>) {
|
||||
block.data.align === 'right' && 'justify-end',
|
||||
block.data.align === 'left' && 'justify-start',
|
||||
style,
|
||||
block.data.fullWidth ? 'max-w-full' : null,
|
||||
block.data.fullWidth ? 'max-w-screen-2xl' : null,
|
||||
'justify-center',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Collection, CustomizationSettings, Space } from '@gitbook/api';
|
||||
import React from 'react';
|
||||
|
||||
import { t } from '@/lib/intl';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { HeaderLogo } from './HeaderLogo';
|
||||
@@ -18,8 +19,17 @@ export function CompactHeader(props: {
|
||||
const { space, collection, customization } = props;
|
||||
|
||||
return (
|
||||
<div className={tcls('flex', 'flex-row', 'items-center')}>
|
||||
<div className={tcls('flex-1')}>
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'lg:flex-col',
|
||||
'flex-wrap',
|
||||
'gap-x-5',
|
||||
'gap-y-3',
|
||||
'justify-between',
|
||||
)}
|
||||
>
|
||||
<div className={tcls('flex-grow-0')}>
|
||||
<HeaderLogo
|
||||
collection={collection}
|
||||
space={space}
|
||||
@@ -30,9 +40,11 @@ export function CompactHeader(props: {
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className={tcls(['ms-2'])}>
|
||||
<div className={tcls('flex-shrink-0', 'sm:grow', 'sm:max-w-xs', 'lg:max-w-full')}>
|
||||
<React.Suspense fallback={null}>
|
||||
<SearchButton />
|
||||
<SearchButton style={[]}>
|
||||
<span>{t({ space }, 'search')}</span>
|
||||
</SearchButton>
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -91,8 +91,8 @@ export function Header(props: {
|
||||
</div>
|
||||
<div className={tcls('flex', 'basis-56', 'grow-0', 'shrink-0')}>
|
||||
<Suspense fallback={null}>
|
||||
<SearchButton style={['bg-header-background-300']}>
|
||||
{t({ space }, 'search')}
|
||||
<SearchButton>
|
||||
<span>{t({ space }, 'search')}</span>
|
||||
</SearchButton>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Collection, CustomizationSettings, Space } from '@gitbook/api';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { HeaderMobileMenu } from '@/components/Header/HeaderMobileMenu';
|
||||
import { Image } from '@/components/utils';
|
||||
import { absoluteHref } from '@/lib/links';
|
||||
import { ClassValue, tcls } from '@/lib/tailwind';
|
||||
@@ -20,21 +21,24 @@ export function HeaderLogo(props: HeaderLogoProps) {
|
||||
const { customization } = props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={absoluteHref('')}
|
||||
className={tcls('group/headerlogo', 'flex', 'flex-row', 'items-center')}
|
||||
>
|
||||
{customization.header.logo ? (
|
||||
<Image
|
||||
alt="Logo"
|
||||
src={customization.header.logo}
|
||||
fetchPriority="high"
|
||||
style={['max-w-50', 'h-8']}
|
||||
/>
|
||||
) : (
|
||||
<LogoFallback {...props} />
|
||||
)}
|
||||
</Link>
|
||||
<div className={tcls('flex', 'flex-row', 'gap-3')}>
|
||||
<HeaderMobileMenu className={tcls('lg:hidden')} />
|
||||
<Link
|
||||
href={absoluteHref('')}
|
||||
className={tcls('group/headerlogo', 'flex', 'flex-row', 'items-center')}
|
||||
>
|
||||
{customization.header.logo ? (
|
||||
<Image
|
||||
alt="Logo"
|
||||
src={customization.header.logo}
|
||||
fetchPriority="high"
|
||||
style={['max-w-50', 'h-8']}
|
||||
/>
|
||||
) : (
|
||||
<LogoFallback {...props} />
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,7 +60,21 @@ function LogoFallback(props: HeaderLogoProps) {
|
||||
style={['w-8', 'h-8']}
|
||||
/>
|
||||
|
||||
<h1 className={tcls('text-lg', 'text-slate-800', 'font-semibold', 'ms-3', textStyle)}>
|
||||
<h1
|
||||
className={tcls(
|
||||
'textwrap-balance',
|
||||
'flex',
|
||||
'leading-tight',
|
||||
'tracking-tight',
|
||||
'max-w-[13ch]',
|
||||
'md:max-w-none',
|
||||
'md:text-lg/tight',
|
||||
'text-slate-800',
|
||||
'font-semibold',
|
||||
'ms-3',
|
||||
textStyle,
|
||||
)}
|
||||
>
|
||||
{collection ? collection.title : space.title}
|
||||
</h1>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import { IconMenu } from '@/components/icons/IconMenu';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
export const HeaderMobileMenu = (props: Partial<React.ButtonHTMLAttributes<HTMLButtonElement>>) => {
|
||||
const toggleNavigation = () => {
|
||||
if (document.body.classList.contains('navigation-visible')) {
|
||||
document.body.classList.remove('navigation-visible');
|
||||
} else {
|
||||
document.body.classList.add('navigation-visible');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<button {...props} onClick={toggleNavigation}>
|
||||
<IconMenu
|
||||
className={tcls(
|
||||
'w-8',
|
||||
'h-8',
|
||||
'bg-dark/2',
|
||||
'rounded',
|
||||
'border',
|
||||
'hover:bg-dark/3',
|
||||
'border-dark/1',
|
||||
'p-[0.25rem]',
|
||||
'dark:bg-light/1',
|
||||
'dark:border-light/1',
|
||||
'dark:hover:bg-light/2',
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -25,11 +25,10 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
|
||||
'flex',
|
||||
'flex-row',
|
||||
'text-sm',
|
||||
section.depth > 1
|
||||
? ['font-light', 'ps-3', 'py-1', 'text-light/5']
|
||||
: ['py-2', 'text-light/7'],
|
||||
'hover:text-primary',
|
||||
section.depth > 1 ? ['ps-3', 'py-1', 'opacity-8'] : ['py-2'],
|
||||
|
||||
activeId === section.id ? 'text-primary' : 'hover:text-light/9',
|
||||
activeId === section.id ? 'text-primary' : '',
|
||||
)}
|
||||
>
|
||||
{section.depth > 1 ? (
|
||||
|
||||
@@ -26,21 +26,29 @@ export function SearchButton(props: { children?: React.ReactNode; style?: ClassV
|
||||
'flex',
|
||||
'flex-1',
|
||||
'flex-row',
|
||||
'justify-center',
|
||||
'items-center',
|
||||
'px-4',
|
||||
'py-1',
|
||||
'rounded',
|
||||
'dark:bg-vanta/4',
|
||||
'hover:bg-light',
|
||||
'px-2',
|
||||
'min-h-[2.5rem]',
|
||||
'w-[2.5rem]',
|
||||
'rounded-lg',
|
||||
'bg-dark/2',
|
||||
'hover:bg-dark/3',
|
||||
'border',
|
||||
'border-dark/2',
|
||||
'shadow-lg',
|
||||
'dark:border-light/3',
|
||||
'border-dark/1',
|
||||
'dark:bg-light/1',
|
||||
'dark:border-light/1',
|
||||
'dark:hover:bg-light/2',
|
||||
'[&>span]:hidden',
|
||||
'sm:justify-between',
|
||||
'sm:[&>span]:flex',
|
||||
'sm:w-full',
|
||||
'sm:px-4',
|
||||
style,
|
||||
)}
|
||||
>
|
||||
<IconSearch className={tcls('w-4', 'h-4', children ? 'me-2' : null)} />
|
||||
{children}
|
||||
<IconSearch className={tcls('w-4', 'h-4')} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ export function SearchModal(props: SearchModalProps) {
|
||||
|
||||
useHotkeys(
|
||||
'ctrl+k, command+k',
|
||||
() => {
|
||||
(e) => {
|
||||
e.preventDefault(); //might be inadvisable as it interferes with expected browser behavior.
|
||||
setQuery('');
|
||||
},
|
||||
[],
|
||||
@@ -51,7 +52,9 @@ export function SearchModal(props: SearchModalProps) {
|
||||
'dark:bg-dark/9',
|
||||
'opacity-100',
|
||||
'z-30',
|
||||
'pt-24',
|
||||
'px-4',
|
||||
'pt-4',
|
||||
'md:pt-[min(12vw,_10rem)]',
|
||||
)}
|
||||
onClick={onClose}
|
||||
>
|
||||
|
||||
@@ -100,7 +100,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={tcls('max-h-[60vh]', 'overflow-auto', 'px')}>
|
||||
<div className={tcls('max-h-[60vh]', 'overflow-auto', 'px', 'inverted-theme-scroll')}>
|
||||
{results.length === 0 ? (
|
||||
<div className={tcls('text-sm', 'text-dark', 'p-6', 'text-center')}>
|
||||
{noResultsMessage}
|
||||
|
||||
@@ -76,7 +76,8 @@ export function SpaceContent(props: {
|
||||
<div
|
||||
className={tcls(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'flex-col',
|
||||
'lg:flex-row',
|
||||
CONTAINER_PADDING,
|
||||
asFullWidth ? null : [CONTAINER_MAX_WIDTH_NORMAL, 'mx-auto'],
|
||||
)}
|
||||
|
||||
@@ -35,9 +35,9 @@ export function PageDocumentItem(props: {
|
||||
? [
|
||||
'font-semibold',
|
||||
'text-primary',
|
||||
'bg-primary-500/3',
|
||||
'hover:bg-primary/3',
|
||||
'dark:text-primary-400',
|
||||
'dark:hover:bg-primary-500/5',
|
||||
'dark:hover:bg-primary-500/3',
|
||||
]
|
||||
: [
|
||||
'font-normal',
|
||||
|
||||
@@ -18,7 +18,7 @@ export function PagesList(props: {
|
||||
const { rootPages, pages, activePage, ancestors, context, style } = props;
|
||||
|
||||
return (
|
||||
<ul className={tcls('flex', 'flex-col', style)}>
|
||||
<ul className={tcls('flex', 'flex-1', 'flex-col', style)}>
|
||||
{pages.map((page) => {
|
||||
if (page.type === 'group') {
|
||||
return (
|
||||
|
||||
@@ -34,24 +34,34 @@ export function TableOfContents(
|
||||
return (
|
||||
<aside
|
||||
className={tcls(
|
||||
'hidden',
|
||||
'lg:flex',
|
||||
'flex',
|
||||
'flex-col',
|
||||
'basis-72',
|
||||
'basis-full',
|
||||
'bg-light',
|
||||
'lg:basis-72',
|
||||
'border-dark/2',
|
||||
'navigation-visible:border-b',
|
||||
'grow-0',
|
||||
'shrink-0',
|
||||
'sticky',
|
||||
'z-[1]',
|
||||
'dark:bg-dark',
|
||||
'dark:border-light/1',
|
||||
withHeaderOffset ? SIDE_COLUMN_WITH_HEADER : SIDE_COLUMN_WITHOUT_HEADER,
|
||||
)}
|
||||
>
|
||||
{header ? <div className={tcls('pt-6', 'pb-3', 'pr-4')}>{header}</div> : null}
|
||||
{header ? <div className={tcls('py-3')}>{header}</div> : null}
|
||||
<div
|
||||
className={tcls(
|
||||
'hidden',
|
||||
'lg:flex',
|
||||
'flex-1',
|
||||
'flex-grow',
|
||||
'overflow-y-auto',
|
||||
'gutter-stable',
|
||||
'navigation-visible:flex', // can be auto height animated as such https://stackoverflow.com/a/76944290
|
||||
header ? 'pt-3' : 'pt-6',
|
||||
'pb-14',
|
||||
'pr-4',
|
||||
'pb-6',
|
||||
)}
|
||||
>
|
||||
<PagesList
|
||||
@@ -62,7 +72,8 @@ export function TableOfContents(
|
||||
context={context}
|
||||
/>
|
||||
</div>
|
||||
<Trademark space={space} />
|
||||
{/* TODO: integrate trademark into mobile menu */}
|
||||
{/* <Trademark space={space} /> */}
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export function ToggleableLinkItem(
|
||||
{children}
|
||||
<Chevron
|
||||
className={tcls(
|
||||
'flex-shrink-0',
|
||||
'w-5',
|
||||
'h-5',
|
||||
'p-0.5',
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { SVGProps } from 'react';
|
||||
|
||||
export function IconMenu(props: Partial<SVGProps<SVGSVGElement>>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
{...props}
|
||||
>
|
||||
<path d="M3 12h18M3 6h18M3 18h18"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
+24
-1
@@ -1,4 +1,5 @@
|
||||
import type { Config } from 'tailwindcss';
|
||||
import plugin from 'tailwindcss/plugin';
|
||||
|
||||
export const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
|
||||
export const opacities = [4, 8, 12, 16, 24, 40, 64, 72, 88, 96];
|
||||
@@ -46,9 +47,31 @@ const config: Config = {
|
||||
'header-background': generateShades('header-background'),
|
||||
'header-link': generateShades('header-link'),
|
||||
},
|
||||
keyframes: {
|
||||
pingAlt: {
|
||||
'0%': {
|
||||
transform: 'scale(0.5)',
|
||||
opacity: '0',
|
||||
},
|
||||
'20%': {
|
||||
opacity: '1',
|
||||
},
|
||||
'30%, 100%': {
|
||||
transform: 'scale(2.5)',
|
||||
opacity: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
boxShadow: {
|
||||
'1xs': '0px 1.54px 1px rgba(0, 0, 0, 0.09), 0px 3.267px 2.754px rgb(0, 0, 0, 0.05), 0px 6.278px 6.63px rgb(0, 0, 0, 0.05), 0px 14px 22px rgb(0, 0, 0, 0.04)',
|
||||
},
|
||||
},
|
||||
opacity: opacity(),
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [
|
||||
plugin(function ({ addVariant }) {
|
||||
addVariant('navigation-visible', 'body.navigation-visible &');
|
||||
}),
|
||||
],
|
||||
};
|
||||
export default config;
|
||||
|
||||
Reference in New Issue
Block a user