Skip to content

Commit 66d0fc0

Browse files
Update hint block (#2840)
Co-authored-by: Valentino Hudhra <[email protected]>
1 parent 05e1d8c commit 66d0fc0

File tree

10 files changed

+284
-145
lines changed

10 files changed

+284
-145
lines changed

.changeset/modern-moons-judge.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gitbook': patch
3+
---
4+
5+
Update design for hint block: use semantic colors (info, warning, danger, success) and add alternative styling for hints with headings

bun.lock

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
},
2525
"packages/colors": {
2626
"name": "@gitbook/colors",
27-
"version": "0.1.0",
27+
"version": "0.2.0",
2828
"devDependencies": {
2929
"typescript": "^5.5.3",
3030
},
@@ -38,9 +38,9 @@
3838
},
3939
"packages/gitbook": {
4040
"name": "gitbook",
41-
"version": "0.6.1",
41+
"version": "0.6.2",
4242
"dependencies": {
43-
"@gitbook/api": "^0.94.0",
43+
"@gitbook/api": "^0.95.0",
4444
"@gitbook/cache-do": "workspace:*",
4545
"@gitbook/colors": "workspace:*",
4646
"@gitbook/emoji-codepoints": "workspace:*",
@@ -155,7 +155,7 @@
155155
},
156156
"packages/openapi-parser": {
157157
"name": "@gitbook/openapi-parser",
158-
"version": "1.0.0",
158+
"version": "1.0.1",
159159
"dependencies": {
160160
"@scalar/openapi-parser": "^0.10.4",
161161
"@scalar/openapi-types": "^0.1.6",
@@ -211,7 +211,7 @@
211211
},
212212
"packages/react-openapi": {
213213
"name": "@gitbook/react-openapi",
214-
"version": "1.0.1",
214+
"version": "1.0.2",
215215
"dependencies": {
216216
"@gitbook/openapi-parser": "workspace:*",
217217
"@scalar/api-client-react": "1.0.87",
@@ -4651,7 +4651,7 @@
46514651

46524652
"gaxios/https-proxy-agent": ["[email protected]", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
46534653

4654-
"gitbook/@gitbook/api": ["@gitbook/api@0.94.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-jOvqUSdyXeuPpBiujkQLb14uVQA5A0XL+P89MmC/53hV7v/8gR8WlJN9RJVDrP0LX51dsLT+/zYN8xWp19nPwA=="],
4654+
"gitbook/@gitbook/api": ["@gitbook/api@0.95.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-9KAbt27Ile6cqAch7QEbiJHALQHojYlhsPzilgdQ5wpHgLwsrd7Smd58A3/8bWBKq4KV0vP4rh3oYhIw+LlWFw=="],
46554655

46564656
"gitbook-v2/next": ["[email protected]", "", { "dependencies": { "@next/env": "15.2.0-canary.45", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.2.0-canary.45", "@next/swc-darwin-x64": "15.2.0-canary.45", "@next/swc-linux-arm64-gnu": "15.2.0-canary.45", "@next/swc-linux-arm64-musl": "15.2.0-canary.45", "@next/swc-linux-x64-gnu": "15.2.0-canary.45", "@next/swc-linux-x64-musl": "15.2.0-canary.45", "@next/swc-win32-arm64-msvc": "15.2.0-canary.45", "@next/swc-win32-x64-msvc": "15.2.0-canary.45", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-UsneTQn9tntbiAaXpvoXhhsTBb58Q2XIs2Dfka+qWA8motBz0ZvW297YHLxhdur4xN0IJvknnZKl5Bs7wAGlOg=="],
46574657

packages/gitbook/e2e/util.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
CustomizationThemeMode,
1515
SiteCustomizationSettings,
1616
} from '@gitbook/api';
17-
import { test, expect, Page, BrowserContext } from '@playwright/test';
17+
import { BrowserContext, expect, Page, test } from '@playwright/test';
1818
import deepMerge from 'deepmerge';
1919
import rison from 'rison';
2020
import { DeepPartial } from 'ts-essentials';
@@ -222,6 +222,10 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
222222
styling: {
223223
theme: newTheme,
224224
primaryColor: { light: '#346DDB', dark: '#346DDB' },
225+
infoColor: { light: '#787878', dark: '#787878' },
226+
warningColor: { light: '#FE9A00', dark: '#FE9A00' },
227+
dangerColor: { light: '#FB2C36', dark: '#FB2C36' },
228+
successColor: { light: '#00C950', dark: '#00C950' },
225229
corners: CustomizationCorners.Rounded,
226230
font: CustomizationFont.Inter,
227231
background: CustomizationBackground.Plain,

packages/gitbook/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static/icons && rm -rf ./public/~gitbook/static/math"
1818
},
1919
"dependencies": {
20-
"@gitbook/api": "^0.94.0",
20+
"@gitbook/api": "^0.95.0",
2121
"@gitbook/cache-do": "workspace:*",
2222
"@gitbook/colors": "workspace:*",
2323
"@gitbook/emoji-codepoints": "workspace:*",

packages/gitbook/src/components/DocumentView/Heading.tsx

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { DocumentBlockHeading } from '@gitbook/api';
1+
import type { DocumentBlockHeading } from '@gitbook/api';
22
import { Icon } from '@gitbook/icons';
33

44
import { tcls } from '@/lib/tailwind';
55

6-
import { BlockProps } from './Block';
6+
import type { BlockProps } from './Block';
77
import { Inlines } from './Inlines';
88
import { getBlockTextStyle } from './spacing';
99

@@ -24,6 +24,7 @@ export function Heading(props: BlockProps<DocumentBlockHeading>) {
2424
>
2525
<div
2626
className={tcls(
27+
'hash',
2728
'grid',
2829
'grid-area-1-1',
2930
'relative',
@@ -41,20 +42,14 @@ export function Heading(props: BlockProps<DocumentBlockHeading>) {
4142
<a
4243
href={`#${id}`}
4344
aria-label="Direct link to heading"
44-
className={tcls(
45-
'inline-flex',
46-
'h-full',
47-
'items-start',
48-
'dark:shadow-none',
49-
'dark:ring-0',
50-
textStyle.lineHeight,
51-
)}
45+
className={tcls('inline-flex', 'h-full', 'items-start', textStyle.lineHeight)}
5246
>
5347
<Icon
5448
icon="hashtag"
5549
className={tcls(
5650
'w-3.5',
57-
'h-[1lh]',
51+
'h-[1em]',
52+
'mt-0.5',
5853
'transition-colors',
5954
'text-transparent',
6055
'group-hover:text-tint-subtle',
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { DocumentBlockHint } from '@gitbook/api';
2-
import { Icon, IconName } from '@gitbook/icons';
1+
import type { DocumentBlockHeading, DocumentBlockHint } from '@gitbook/api';
2+
import { Icon, type IconName } from '@gitbook/icons';
33
import React from 'react';
44

5-
import { ClassValue, tcls } from '@/lib/tailwind';
5+
import { type ClassValue, tcls } from '@/lib/tailwind';
66

7-
import { BlockProps } from './Block';
7+
import { Block, type BlockProps } from './Block';
88
import { Blocks } from './Blocks';
99
import { getBlockTextStyle } from './spacing';
1010

@@ -13,119 +13,143 @@ export function Hint(props: BlockProps<DocumentBlockHint>) {
1313
const hintStyle = HINT_STYLES[block.data.style] ?? HINT_STYLES.info;
1414
const firstLine = getBlockTextStyle(block.nodes[0]);
1515

16+
const firstNode = block.nodes[0];
17+
const hasHeading = ['heading-1', 'heading-2', 'heading-3'].includes(block.nodes[0].type);
18+
1619
return (
1720
<div
1821
className={tcls(
1922
'hint',
20-
'p-4',
2123
'transition-colors',
2224
'rounded-md',
25+
hasHeading ? 'rounded-l' : null,
2326
'straight-corners:rounded-none',
24-
hintStyle.style,
27+
'overflow-hidden',
28+
hasHeading ? ['border-l-2', hintStyle.containerWithHeader] : hintStyle.container,
29+
30+
'text-sm',
31+
32+
'grid',
33+
'grid-cols-[auto_1fr]',
34+
hasHeading ? 'grid-rows-[auto_auto]' : '',
35+
2536
style,
2637
)}
2738
>
28-
<div className={tcls('flex', 'flex-row')}>
39+
<div
40+
className={tcls(
41+
'py-3',
42+
'pl-3',
43+
hasHeading ? hintStyle.header : null,
44+
hintStyle.iconColor,
45+
)}
46+
>
2947
<Icon
3048
icon={hintStyle.icon}
31-
className={tcls(
32-
'size-5',
33-
'mr-4',
34-
'mt-0.5',
35-
firstLine.lineHeight,
36-
hintStyle.iconColor,
37-
)}
49+
className={tcls('size-[1.2em]', 'mt-0.5', firstLine.lineHeight)}
3850
/>
39-
<Blocks
40-
{...contextProps}
41-
ancestorBlocks={[...ancestorBlocks, block]}
42-
nodes={block.nodes}
43-
blockStyle={tcls(
44-
hintStyle.bodyColor,
45-
// render hash icon on the other side of the heading
46-
'flip-heading-hash',
51+
</div>
52+
{hasHeading ? (
53+
<Block
54+
style={tcls(
55+
'text-[1em] *:mt-0 p-3 flip-heading-hash',
56+
hasHeading ? hintStyle.header : null,
4757
)}
48-
style={['flex-1', 'space-y-4', '[&_.hint]:border', '[&_pre]:border']}
58+
ancestorBlocks={[...ancestorBlocks, block]}
59+
{...contextProps}
60+
block={firstNode}
4961
/>
50-
</div>
62+
) : null}
63+
<Blocks
64+
{...contextProps}
65+
ancestorBlocks={[...ancestorBlocks, block]}
66+
nodes={hasHeading ? block.nodes.slice(1) : block.nodes}
67+
blockStyle={tcls(
68+
hintStyle.body,
69+
// render hash icon on the other side of the heading
70+
'flip-heading-hash',
71+
)}
72+
style={[
73+
'p-3',
74+
'empty:p-0',
75+
'-row-end-1',
76+
'-col-end-1',
77+
'space-y-4',
78+
'[&_.hint]:border',
79+
'[&_pre]:border',
80+
'[&_pre]:border-neutral',
81+
]}
82+
/>
5183
</div>
5284
);
5385
}
5486

5587
const HINT_STYLES: {
5688
[style in DocumentBlockHint['data']['style']]: {
5789
icon: IconName;
58-
iconColor: ClassValue;
59-
bodyColor: ClassValue;
60-
style: ClassValue;
90+
iconColor?: ClassValue;
91+
body?: ClassValue;
92+
header?: ClassValue;
93+
container?: ClassValue;
94+
containerWithHeader?: ClassValue;
6195
};
6296
} = {
6397
info: {
6498
icon: 'circle-info',
65-
iconColor: ['text-primary'],
66-
bodyColor: ['[&_a]:text-primary', '[&_a:hover]:text-primary-strong'],
67-
style: [
68-
'bg-tint',
69-
'print-mode:!bg-tint',
70-
'theme-muted:bg-tint-base',
71-
'theme-bold-tint:bg-tint-base',
72-
'theme-gradient:bg-tint-12/1',
73-
'border-tint',
74-
'[&_.can-override-bg]:bg-tint-active',
75-
'[&_.can-override-text]:text-tint-strong',
99+
iconColor: 'text-info-subtle contrast-more:text-info',
100+
header: 'bg-info-active',
101+
body: [
102+
'text-neutral-strong',
103+
'[&_.can-override-bg]:bg-neutral-active',
104+
'[&_.can-override-text]:text-neutral-strong',
76105
],
106+
container:
107+
'bg-neutral theme-muted:bg-neutral-base theme-bold-tint:bg-neutral-base theme-gradient:bg-neutral-12/1 border-neutral',
108+
containerWithHeader: 'border-info-solid bg-neutral-subtle theme-gradient:bg-neutral-12/1',
77109
},
78110
warning: {
79111
icon: 'circle-exclamation',
80-
iconColor: ['text-amber-500', 'dark:text-orange-400'], // Darker shades of orange-* mismatch with lighter shades, so in light mode we use amber text on top of orange bg.
81-
bodyColor: [
82-
'text-orange-950',
83-
'dark:text-orange-50',
84-
'[&_a]:text-orange-800',
85-
'[&_a:hover]:text-orange-900',
86-
'dark:[&_a]:text-orange-400',
87-
'dark:[&_a:hover]:text-orange-300',
88-
'[&_.can-override-bg]:bg-orange-500/3',
89-
'[&_.can-override-text]:text-orange-800',
90-
'dark:[&_.can-override-text]:text-orange-400',
91-
'decoration-orange-800/6',
92-
'dark:decoration-orange-400/6',
112+
iconColor: 'text-warning-subtle contrast-more:text-warning',
113+
header: 'bg-warning-active',
114+
body: [
115+
'text-neutral-strong',
116+
'[&_a]:text-warning',
117+
'[&_a:hover]:text-warning-strong',
118+
'[&_.can-override-bg]:bg-warning-active',
119+
'[&_.can-override-text]:text-warning-strong',
120+
'decoration-warning/6',
93121
],
94-
style: ['bg-orange-500/2', 'border-orange-500/4'],
122+
container: 'bg-warning border-warning',
123+
containerWithHeader: 'border-warning-solid bg-warning-subtle',
95124
},
96125
danger: {
97126
icon: 'triangle-exclamation',
98-
iconColor: ['text-red-500', 'dark:text-red-400'],
99-
bodyColor: [
100-
'text-red-950',
101-
'dark:text-red-50',
102-
'[&_a]:text-red-800',
103-
'[&_a:hover]:text-red-900',
104-
'dark:[&_a]:text-red-400',
105-
'dark:[&_a:hover]:text-red-300',
106-
'[&_.can-override-bg]:bg-red-500/3',
107-
'[&_.can-override-text]:text-red-400',
108-
'decoration-red-800/6',
109-
'dark:decoration-red-400/6',
127+
iconColor: 'text-danger-subtle contrast-more:text-danger',
128+
header: 'bg-danger-active',
129+
body: [
130+
'text-neutral-strong',
131+
'[&_a]:text-danger',
132+
'[&_a:hover]:text-danger-strong',
133+
'[&_.can-override-bg]:bg-danger-active',
134+
'[&_.can-override-text]:text-danger-strong',
135+
'decoration-danger/6',
110136
],
111-
style: ['bg-red-500/2', 'border-red-500/4'],
137+
container: 'bg-danger border-danger',
138+
containerWithHeader: 'border-danger-solid bg-danger-subtle',
112139
},
113140
success: {
114141
icon: 'circle-check',
115-
iconColor: ['text-green-500', 'dark:text-green-400'],
116-
bodyColor: [
117-
'text-green-950',
118-
'dark:text-green-50',
119-
'[&_a]:text-green-800',
120-
'[&_a:hover]:text-green-900',
121-
'dark:[&_a]:text-green-400',
122-
'dark:[&_a:hover]:text-green-300',
123-
'[&_.can-override-bg]:bg-green-500/3',
124-
'[&_.can-override-text]:text-green-800',
125-
'dark:[&_.can-override-text]:text-green-400',
126-
'decoration-green-800/6',
127-
'dark:decoration-green-400/6',
142+
iconColor: 'text-success-subtle contrast-more:text-success',
143+
header: 'bg-success-active',
144+
body: [
145+
'text-neutral-strong',
146+
'[&_a]:text-success',
147+
'[&_a:hover]:text-success-strong',
148+
'[&_.can-override-bg]:bg-success-active',
149+
'[&_.can-override-text]:text-success-strong',
150+
'decoration-success/6',
128151
],
129-
style: ['bg-green-500/2', 'border-green-500/4'],
152+
container: 'bg-success border-success',
153+
containerWithHeader: 'border-success-solid bg-success-subtle',
130154
},
131155
};

0 commit comments

Comments
 (0)