mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-14 06:35:17 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a3fdcfd3c | |||
| a3ddf7d575 | |||
| 447c6f602f | |||
| 71e52f7b0d | |||
| 6703dae4ea |
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"generate": "rm -rf ./public && cp -r ../gitbook/public ./public",
|
||||
"dev:v2": "env-cmd --silent -f ../../.env.local next --turbopack",
|
||||
"dev:v2": "env-cmd --silent -f ../../.env.local next",
|
||||
"build": "next build",
|
||||
"build:v2": "next build",
|
||||
"start": "next start",
|
||||
|
||||
@@ -893,7 +893,7 @@ const testCases: TestsCase[] = [
|
||||
name: 'Navigation to shared space',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
const sharedSpaceLink = page.locator('a.underline');
|
||||
const sharedSpaceLink = page.locator('a.styled-link');
|
||||
await sharedSpaceLink.click();
|
||||
await expect(
|
||||
page.getByRole('heading', { level: 1, name: 'shared' })
|
||||
@@ -914,7 +914,7 @@ const testCases: TestsCase[] = [
|
||||
name: 'Navigation to shared space',
|
||||
url: '',
|
||||
run: async (page) => {
|
||||
await page.locator('a.underline').click();
|
||||
await page.locator('a.styled-link').click();
|
||||
await expect(
|
||||
page.getByRole('heading', { level: 1, name: 'shared' })
|
||||
).toBeVisible();
|
||||
|
||||
@@ -7,12 +7,14 @@ import {
|
||||
import { LinkBox, LinkOverlay } from '@/components/primitives';
|
||||
import { Image } from '@/components/utils';
|
||||
import { resolveContentRef } from '@/lib/references';
|
||||
import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { RecordColumnValue } from './RecordColumnValue';
|
||||
import type { TableRecordKV, TableViewProps } from './Table';
|
||||
import { getRecordValue } from './utils';
|
||||
|
||||
import styles from './recordCard.module.css';
|
||||
|
||||
export async function RecordCard(
|
||||
props: TableViewProps<DocumentTableViewCards> & {
|
||||
record: TableRecordKV;
|
||||
@@ -143,35 +145,20 @@ export async function RecordCard(
|
||||
</div>
|
||||
);
|
||||
|
||||
const style = [
|
||||
'group',
|
||||
'grid',
|
||||
'shadow-1xs',
|
||||
'shadow-tint-9/1',
|
||||
'depth-flat:shadow-none',
|
||||
'rounded',
|
||||
'straight-corners:rounded-none',
|
||||
'circular-corners:rounded-xl',
|
||||
'dark:shadow-transparent',
|
||||
|
||||
'before:pointer-events-none',
|
||||
'before:grid-area-1-1',
|
||||
'before:transition-shadow',
|
||||
'before:w-full',
|
||||
'before:h-full',
|
||||
'before:rounded-[inherit]',
|
||||
'before:ring-1',
|
||||
'before:ring-tint-12/2',
|
||||
'before:z-10',
|
||||
'before:relative',
|
||||
] as ClassValue;
|
||||
|
||||
if (target && targetRef) {
|
||||
return (
|
||||
// We don't use `Link` directly here because we could end up in a situation where
|
||||
// a link is rendered inside a link, which is not allowed in HTML.
|
||||
// It causes an hydration error in React.
|
||||
<LinkBox href={target.href} className={tcls(style, 'hover:before:ring-tint-12/5')}>
|
||||
<LinkBox
|
||||
href={target.href}
|
||||
className={tcls(
|
||||
styles.card,
|
||||
'hover:before:ring-tint-12/5',
|
||||
'group',
|
||||
'before:grid-area-1-1'
|
||||
)}
|
||||
>
|
||||
<LinkOverlay
|
||||
href={target.href}
|
||||
insights={{
|
||||
@@ -187,5 +174,5 @@ export async function RecordCard(
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={tcls(style)}>{body}</div>;
|
||||
return <div className={tcls('group', styles.card)}>{body}</div>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.card {
|
||||
@apply grid shadow-1xs shadow-tint-9/1 depth-flat:shadow-none rounded straight-corners:rounded-none circular-corners:rounded-xl dark:shadow-transparent before:pointer-events-none before:transition-shadow before:w-full before:h-full before:rounded-[inherit] before:ring-1 before:ring-tint-12/2 before:z-10 before:relative;
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import type { GitBookSiteContext } from '@v2/lib/context';
|
||||
import { PageIcon } from '../PageIcon';
|
||||
import { StyledLink } from '../primitives';
|
||||
|
||||
import styles from './pageBody.module.css';
|
||||
|
||||
export async function PageHeader(props: {
|
||||
context: GitBookSiteContext;
|
||||
page: RevisionPageDocument;
|
||||
@@ -40,22 +42,7 @@ export async function PageHeader(props: {
|
||||
return (
|
||||
<Fragment key={breadcrumb.id}>
|
||||
<li key={breadcrumb.id}>
|
||||
<StyledLink
|
||||
href={href}
|
||||
className={tcls(
|
||||
'no-underline',
|
||||
'hover:underline',
|
||||
'text-xs',
|
||||
'tracking-wide',
|
||||
'font-semibold',
|
||||
'uppercase',
|
||||
'flex',
|
||||
'items-center',
|
||||
'gap-1.5',
|
||||
'contrast-more:underline',
|
||||
'contrast-more:decoration-current'
|
||||
)}
|
||||
>
|
||||
<StyledLink href={href} className={styles.headerLink}>
|
||||
<PageIcon
|
||||
page={breadcrumb}
|
||||
style="flex size-4 items-center justify-center text-base leading-none"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.headerLink {
|
||||
@apply no-underline hover:underline text-xs tracking-wide font-semibold uppercase flex items-center gap-1.5 contrast-more:underline contrast-more:decoration-current;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { TOCPageIcon } from './TOCPageIcon';
|
||||
|
||||
import styles from './toc.module.css';
|
||||
|
||||
export async function PageLinkItem(props: { page: RevisionPageLink; context: GitBookSiteContext }) {
|
||||
const { page, context } = props;
|
||||
|
||||
@@ -17,25 +19,7 @@ export async function PageLinkItem(props: { page: RevisionPageLink; context: Git
|
||||
<li className={tcls('flex', 'flex-col')}>
|
||||
<Link
|
||||
href={resolved?.href ?? '#'}
|
||||
className={tcls(
|
||||
'flex',
|
||||
'justify-start',
|
||||
'items-center',
|
||||
'gap-3',
|
||||
'p-1.5',
|
||||
'pl-3',
|
||||
'text-sm',
|
||||
'transition-colors',
|
||||
'duration-100',
|
||||
'text-tint-strong/7',
|
||||
'rounded-md',
|
||||
'straight-corners:rounded-none',
|
||||
'circular-corners:rounded-xl',
|
||||
'before:content-none',
|
||||
'font-normal',
|
||||
'hover:bg-tint',
|
||||
'hover:text-tint-strong'
|
||||
)}
|
||||
className={styles.pageLinkItem}
|
||||
insights={{
|
||||
type: 'link_click',
|
||||
link: {
|
||||
|
||||
@@ -10,6 +10,8 @@ import { useCurrentPagePath } from '../hooks';
|
||||
import { Link, type LinkInsightsProps, type LinkProps } from '../primitives';
|
||||
import { useScrollToActiveTOCItem } from './TOCScroller';
|
||||
|
||||
import styles from './toc.module.css';
|
||||
|
||||
/**
|
||||
* Client component for a page document to toggle its children and be marked as active.
|
||||
*/
|
||||
@@ -67,40 +69,12 @@ function LinkItem(
|
||||
insights={insights}
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
className={tcls(
|
||||
'group/toclink toclink relative transition-colors',
|
||||
'flex flex-row justify-between',
|
||||
'circular-corners:rounded-2xl rounded-md straight-corners:rounded-none p-1.5 pl-3',
|
||||
'text-balance font-normal text-sm text-tint-strong/7 hover:bg-tint-hover hover:text-tint-strong contrast-more:text-tint-strong',
|
||||
'hover:contrast-more:text-tint-strong hover:contrast-more:ring-1 hover:contrast-more:ring-tint-12',
|
||||
'before:contents[] before:-left-px before:absolute before:inset-y-0',
|
||||
'sidebar-list-line:rounded-l-none sidebar-list-line:before:w-px sidebar-list-default:[&+div_a]:rounded-l-none [&+div_a]:pl-5 sidebar-list-default:[&+div_a]:before:w-px',
|
||||
'group/toclink',
|
||||
'toclink',
|
||||
'before:contents[]',
|
||||
styles.toggleableLink,
|
||||
|
||||
isActive && [
|
||||
'font-semibold',
|
||||
'sidebar-list-line:before:w-0.5',
|
||||
|
||||
'before:bg-primary-solid',
|
||||
'text-primary-subtle',
|
||||
'contrast-more:text-primary',
|
||||
|
||||
'sidebar-list-pill:bg-primary',
|
||||
'[html.sidebar-list-pill.theme-muted_&]:bg-primary-hover',
|
||||
'[html.sidebar-list-pill.theme-bold.tint_&]:bg-primary-hover',
|
||||
'[html.sidebar-filled.sidebar-list-pill.theme-muted_&]:bg-primary',
|
||||
'[html.sidebar-filled.sidebar-list-pill.theme-bold.tint_&]:bg-primary',
|
||||
|
||||
'hover:bg-primary-hover',
|
||||
'hover:text-primary',
|
||||
'hover:before:bg-primary-solid-hover',
|
||||
'sidebar-list-pill:hover:bg-primary-hover',
|
||||
|
||||
'contrast-more:text-primary',
|
||||
'contrast-more:hover:text-primary-strong',
|
||||
'contrast-more:bg-primary',
|
||||
'contrast-more:ring-1',
|
||||
'contrast-more:ring-primary',
|
||||
'contrast-more:hover:ring-primary-hover',
|
||||
]
|
||||
isActive && styles.active
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
.pageLinkItem {
|
||||
@apply flex
|
||||
justify-start
|
||||
items-center
|
||||
gap-3
|
||||
p-1.5
|
||||
pl-3
|
||||
text-sm
|
||||
transition-colors
|
||||
duration-100
|
||||
text-tint-strong/7
|
||||
rounded-md
|
||||
straight-corners:rounded-none
|
||||
circular-corners:rounded-xl
|
||||
before:content-none
|
||||
font-normal
|
||||
hover:bg-tint
|
||||
hover:text-tint-strong;
|
||||
}
|
||||
|
||||
.toggleableLink {
|
||||
@apply relative transition-colors
|
||||
flex flex-row justify-between
|
||||
circular-corners:rounded-2xl rounded-md straight-corners:rounded-none p-1.5 pl-3
|
||||
text-balance font-normal text-sm text-tint-strong/7 hover:bg-tint-hover hover:text-tint-strong contrast-more:text-tint-strong
|
||||
hover:contrast-more:text-tint-strong hover:contrast-more:ring-1 hover:contrast-more:ring-tint-12
|
||||
before:-left-px before:absolute before:inset-y-0
|
||||
sidebar-list-line:rounded-l-none sidebar-list-line:before:w-px sidebar-list-default:[&+div_a]:rounded-l-none [&+div_a]:pl-5 sidebar-list-default:[&+div_a]:before:w-px;
|
||||
}
|
||||
|
||||
.active {
|
||||
@apply font-semibold
|
||||
sidebar-list-line:before:w-0.5
|
||||
|
||||
before:bg-primary-solid
|
||||
text-primary-subtle
|
||||
|
||||
sidebar-list-pill:bg-primary
|
||||
[html.sidebar-list-pill.theme-muted_&]:bg-primary-hover
|
||||
[html.sidebar-list-pill.theme-bold.tint_&]:bg-primary-hover
|
||||
[html.sidebar-filled.sidebar-list-pill.theme-muted_&]:bg-primary
|
||||
[html.sidebar-filled.sidebar-list-pill.theme-bold.tint_&]:bg-primary
|
||||
|
||||
hover:bg-primary-hover
|
||||
hover:text-primary
|
||||
hover:before:bg-primary-solid-hover
|
||||
sidebar-list-pill:hover:bg-primary-hover
|
||||
|
||||
contrast-more:text-primary
|
||||
contrast-more:hover:text-primary-strong
|
||||
contrast-more:bg-primary
|
||||
contrast-more:ring-1
|
||||
contrast-more:ring-primary
|
||||
contrast-more:hover:ring-primary-hover;
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
import { Icon, type IconName } from '@gitbook/icons';
|
||||
import { Link, type LinkInsightsProps } from './Link';
|
||||
|
||||
import styles from './button.module.css';
|
||||
|
||||
type ButtonProps = {
|
||||
href?: string;
|
||||
variant?: 'primary' | 'secondary' | 'blank';
|
||||
@@ -19,33 +21,14 @@ type ButtonProps = {
|
||||
HTMLAttributes<HTMLElement>;
|
||||
|
||||
const variantClasses = {
|
||||
primary: [
|
||||
'bg-primary-solid',
|
||||
'text-contrast-primary-solid',
|
||||
'hover:bg-primary-solid-hover',
|
||||
'hover:text-contrast-primary-solid-hover',
|
||||
'ring-0',
|
||||
'contrast-more:ring-1',
|
||||
],
|
||||
blank: [
|
||||
'bg-transparent',
|
||||
'text-tint',
|
||||
'ring-0',
|
||||
'shadow-none',
|
||||
'hover:bg-primary-hover',
|
||||
'hover:text-primary',
|
||||
'hover:scale-1',
|
||||
'hover:shadow-none',
|
||||
'contrast-more:bg-tint-subtle',
|
||||
],
|
||||
primary: [styles.primary],
|
||||
blank: [styles.blank, 'hover:scale-1'],
|
||||
secondary: [
|
||||
'bg-tint',
|
||||
'depth-flat:bg-transparent',
|
||||
'text-tint',
|
||||
'hover:bg-tint-hover',
|
||||
'depth-flat:hover:bg-tint-hover',
|
||||
'hover:text-primary',
|
||||
'contrast-more:bg-tint-subtle',
|
||||
styles.secondary,
|
||||
'bg-secondary-solid',
|
||||
'text-contrast-secondary-solid',
|
||||
'hover:bg-secondary-solid-hover',
|
||||
'hover:text-contrast-secondary-solid-hover',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -62,50 +45,14 @@ export function Button({
|
||||
...rest
|
||||
}: ButtonProps & { target?: HTMLAttributeAnchorTarget }) {
|
||||
const sizes = {
|
||||
default: ['text-base', 'font-semibold', 'px-5', 'py-2', 'circular-corners:px-6'],
|
||||
medium: ['text-sm', 'px-3.5', 'py-1.5', 'circular-corners:px-4'],
|
||||
small: ['text-xs', 'py-2', iconOnly ? 'px-2' : 'px-3'],
|
||||
default: [styles.default],
|
||||
medium: [styles.medium],
|
||||
small: [styles.small, iconOnly ? 'px-2' : 'px-3'],
|
||||
};
|
||||
|
||||
const sizeClasses = sizes[size] || sizes.default;
|
||||
|
||||
const domClassName = tcls(
|
||||
'button',
|
||||
'inline-flex',
|
||||
'items-center',
|
||||
'gap-2',
|
||||
'rounded-md',
|
||||
'straight-corners:rounded-none',
|
||||
'circular-corners:rounded-full',
|
||||
// 'place-self-start',
|
||||
|
||||
'ring-1',
|
||||
'ring-tint',
|
||||
'hover:ring-tint-hover',
|
||||
|
||||
'shadow-sm',
|
||||
'shadow-tint',
|
||||
'dark:shadow-tint-1',
|
||||
'hover:shadow-md',
|
||||
'active:shadow-none',
|
||||
'depth-flat:shadow-none',
|
||||
|
||||
'contrast-more:ring-tint-12',
|
||||
'contrast-more:hover:ring-2',
|
||||
'contrast-more:hover:ring-tint-12',
|
||||
|
||||
'hover:scale-104',
|
||||
'depth-flat:hover:scale-100',
|
||||
'active:scale-100',
|
||||
'transition-all',
|
||||
|
||||
'grow-0',
|
||||
'shrink-0',
|
||||
'truncate',
|
||||
variantClasses[variant],
|
||||
sizeClasses,
|
||||
className
|
||||
);
|
||||
const domClassName = tcls(styles.styledButton, variantClasses[variant], sizeClasses, className);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,8 @@ import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Link, type LinkInsightsProps } from './Link';
|
||||
|
||||
import styles from './card.module.css';
|
||||
|
||||
export async function Card(
|
||||
props: {
|
||||
href: string;
|
||||
@@ -17,28 +19,7 @@ export async function Card(
|
||||
const { title, leadingIcon, href, preTitle, postTitle, style, insights } = props;
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={tcls(
|
||||
'group',
|
||||
'flex',
|
||||
'flex-row',
|
||||
'justify-between',
|
||||
'items-center',
|
||||
'gap-4',
|
||||
'ring-1',
|
||||
'ring-tint-subtle',
|
||||
'rounded',
|
||||
'straight-corners:rounded-none',
|
||||
'circular-corners:rounded-2xl',
|
||||
'px-5',
|
||||
'py-3',
|
||||
'transition-shadow',
|
||||
'hover:ring-primary-hover',
|
||||
style
|
||||
)}
|
||||
insights={insights}
|
||||
>
|
||||
<Link href={href} className={tcls('group', styles.styledCard, style)} insights={insights}>
|
||||
{leadingIcon}
|
||||
<span className={tcls('flex', 'flex-col', 'flex-1')}>
|
||||
{preTitle ? (
|
||||
|
||||
@@ -2,6 +2,8 @@ import { type ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
import { Link, type LinkProps } from '../primitives/Link';
|
||||
|
||||
import styles from './styledLink.module.css';
|
||||
|
||||
export const linkStyles = [
|
||||
'underline',
|
||||
'decoration-[max(0.07em,1px)]', // Set the underline to be proportional to the font size, with a minimum. The default is too thin.
|
||||
@@ -29,7 +31,7 @@ export function StyledLink(props: Omit<LinkProps, 'style'> & { className?: Class
|
||||
const { className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Link {...rest} className={tcls(linkStyles, className)}>
|
||||
<Link {...rest} className={tcls(styles.styledLink, className)}>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
.styledButton {
|
||||
@apply inline-flex items-center gap-2 rounded-md straight-corners:rounded-none circular-corners:rounded-full ring-1 ring-tint hover:ring-tint-hover shadow-sm shadow-tint dark:shadow-tint-1 hover:shadow-md active:shadow-none depth-flat:shadow-none contrast-more:ring-tint-12 contrast-more:hover:ring-2 contrast-more:hover:ring-tint-12 hover:scale-104 depth-flat:hover:scale-100 active:scale-100 transition-all grow-0 shrink-0 truncate;
|
||||
}
|
||||
|
||||
.primary {
|
||||
@apply bg-primary-solid
|
||||
text-contrast-primary-solid
|
||||
hover:bg-primary-solid-hover
|
||||
hover:text-contrast-primary-solid-hover
|
||||
ring-0
|
||||
contrast-more:ring-1;
|
||||
}
|
||||
|
||||
.blank {
|
||||
@apply bg-transparent
|
||||
text-tint
|
||||
ring-0
|
||||
shadow-none
|
||||
hover:bg-primary-hover
|
||||
hover:text-primary
|
||||
hover:shadow-none
|
||||
contrast-more:bg-tint-subtle;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
@apply ring-0
|
||||
contrast-more:ring-1;
|
||||
}
|
||||
|
||||
.default {
|
||||
@apply text-base font-semibold px-5 py-2 circular-corners:px-6;
|
||||
}
|
||||
|
||||
.medium {
|
||||
@apply text-sm px-3.5 py-1.5 circular-corners:px-4;
|
||||
}
|
||||
|
||||
.small {
|
||||
@apply text-xs py-2;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.styledCard {
|
||||
@apply flex flex-row justify-between items-center gap-4 ring-1 ring-tint-subtle rounded straight-corners:rounded-none circular-corners:rounded-2xl px-5 py-3 transition-shadow hover:ring-primary-hover;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.styledLink {
|
||||
@apply underline decoration-[max(0.07em,1px)] underline-offset-2 links-accent:underline-offset-4 links-default:decoration-primary/6 links-default:text-primary-subtle links-default:hover:text-primary-strong links-default:contrast-more:text-primary links-default:contrast-more:hover:text-primary-strong links-accent:decoration-primary-subtle links-accent:hover:decoration-[3px] links-accent:hover:[text-decoration-skip-ink:none] transition-all duration-100;
|
||||
}
|
||||
Reference in New Issue
Block a user