Skip to content

Upgrade to Tailwind v4 #3206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Aug 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
39c2e47
Upgrade packages
zenoachtig May 1, 2025
f525d8a
Update files
zenoachtig May 1, 2025
e3e17d6
Tiny fixes
zenoachtig May 1, 2025
a676a3d
Undo turbopack removal
zenoachtig May 1, 2025
e012d45
Changeset
zenoachtig May 1, 2025
bbe41d3
Format
zenoachtig May 1, 2025
df57335
Fix `table.module.css`
zenoachtig May 1, 2025
5dd306b
Fix code blocks & API blocks
zenoachtig May 1, 2025
26a7802
Scope Scalar import to `.scalar-container`
zenoachtig May 1, 2025
0d3a804
Give `button`s back their hover cursor
zenoachtig May 1, 2025
4b52b3d
Remove scrollbar styling on Safari
zenoachtig May 1, 2025
9616688
Fix ContentKit
zenoachtig May 1, 2025
b451442
Fix details component
zenoachtig May 1, 2025
a419782
Update button cursor
zenoachtig May 1, 2025
a70e757
Fix keyboard shortcut
zenoachtig May 1, 2025
f0a4ce0
Fix MathJax inlines
zenoachtig May 1, 2025
2aff37b
Merge branch 'main' into upgrade-tailwind-v4
zenoachtig Jul 23, 2025
ad73bba
Finish merge
zenoachtig Jul 23, 2025
5353d81
Fix `Button` iconOnly padding
zenoachtig Jul 23, 2025
3b8eef3
Fix AI response feedback button
zenoachtig Jul 23, 2025
ed538f9
Format
zenoachtig Jul 23, 2025
8f3a47d
Update changeset
zenoachtig Jul 23, 2025
27c2064
Clean up aside
zenoachtig Jul 23, 2025
6a65d19
Fix OpenAPI (divide styles were missing)
zenoachtig Jul 23, 2025
6c304c4
Fix heading spacing
zenoachtig Jul 23, 2025
564a089
Reduce button shadow
zenoachtig Jul 23, 2025
1a2ee3d
Fix Shiki tokens
zenoachtig Jul 23, 2025
b8cf7c4
Merge branch 'main' into upgrade-tailwind-v4
zenoachtig Jul 23, 2025
794a4ea
Merge branch 'main' into upgrade-tailwind-v4
zenoachtig Jul 25, 2025
9d41b2f
Change margins to make migration 1:1
zenoachtig Jul 25, 2025
36e566a
Reduce sidebar padding
zenoachtig Jul 25, 2025
38afb78
Fix page header to be 1:1 with previous version
zenoachtig Jul 25, 2025
115ecdf
Shift back copy button for 1:1
zenoachtig Jul 25, 2025
14cfab9
Merge branch 'main' into upgrade-tailwind-v4
zenoachtig Aug 1, 2025
2aee0e6
Update bun.lock
zenoachtig Aug 1, 2025
a026ae2
Update Button.tsx
zenoachtig Aug 1, 2025
f96fb4e
Spacing
zenoachtig Aug 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/twenty-teachers-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@gitbook/react-math": patch
"gitbook": patch
"@gitbook/icons": patch
---

Upgrade to Tailwind v4
174 changes: 125 additions & 49 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/gitbook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"devDependencies": {
"@argos-ci/playwright": "^5.0.5",
"@playwright/test": "^1.51.1",
"@tailwindcss/postcss": "^4.1.11",
"@types/js-cookie": "^3.0.6",
"@types/jsontoxml": "^1.0.5",
"@types/jsonwebtoken": "^9.0.6",
Expand All @@ -84,14 +85,13 @@
"@types/react": "18.3.13",
"@types/react-dom": "18.3.1",
"@types/rison": "^0.0.9",
"autoprefixer": "^10",
"deepmerge": "^4.3.1",
"env-cmd": "^10.1.0",
"jsonwebtoken": "^9.0.2",
"postcss": "^8",
"psi": "^4.1.0",
"stylelint": "^16.16.0",
"tailwindcss": "^3.4.0",
"tailwindcss": "^4.1.11",
"ts-essentials": "^10.0.1",
"typescript": "^5.5.3",
"vercel": "^39.3.0"
Expand Down
3 changes: 1 addition & 2 deletions packages/gitbook/postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
'@tailwindcss/postcss': {},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function AIMessageView(
<div
key={index}
className={tcls(
'flex animate-fadeIn-slow flex-col gap-2',
'flex animate-fade-in-slow flex-col gap-2',
step.content.nodes.length > 0 ? 'has-content' : ''
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function ToolCallSummary(props: { toolCall: AIToolCall; context: GitBookSiteCont
const { toolCall, context } = props;

return (
<div className="flex origin-left animate-scaleIn-slow items-start gap-2 text-sm text-tint-subtle">
<div className="flex origin-left animate-scale-in-slow items-start gap-2 text-sm text-tint-subtle">
<Icon
icon={getIconForToolCall(toolCall)}
className="mt-1 size-3 shrink-0 text-tint-subtle/8"
Expand Down Expand Up @@ -175,12 +175,12 @@ async function DescriptionForSearchToolCall(props: {
) : null}
</summary>
{hasResults ? (
<div className="mt-1 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all duration-500 [transition-behavior:allow-discrete] group-open:max-h-96 group-open:opacity-11">
<div className="mt-1 max-h-0 overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-lg border border-tint-subtle p-2 opacity-0 transition-all transition-discrete duration-500 group-open:max-h-96 group-open:opacity-11">
<ol className="space-y-1">
{searchResultsWithHrefs.map((result, index) => (
<li
key={`${result.pageId}-${index}`}
className="animate-fadeIn-slow"
className="animate-fade-in-slow"
style={{
animationDelay: `${index * 25}ms`,
}}
Expand Down
12 changes: 5 additions & 7 deletions packages/gitbook/src/components/AIChat/AIChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export function AIChatWindow(props: {
return (
<div
data-testid="ai-chat"
className="ai-chat inset-y-0 right-0 z-40 mx-auto flex max-w-3xl animate-present scroll-mt-36 px-4 py-4 transition-all duration-300 sm:px-6 lg:fixed lg:w-80 lg:animate-enterFromRight lg:pr-4 lg:pl-0 xl:w-96"
className="ai-chat inset-y-0 right-0 z-40 mx-auto flex max-w-3xl animate-present scroll-mt-36 px-4 py-4 transition-all duration-300 sm:px-6 lg:fixed lg:w-80 lg:animate-enter-from-right lg:pr-4 lg:pl-0 xl:w-96"
ref={containerRef}
>
<div className="relative flex h-full grow flex-col overflow-hidden circular-corners:rounded-3xl rounded-corners:rounded-md bg-tint-base text-sm text-tint depth-subtle:shadow-lg shadow-tint ring-1 ring-tint-subtle">
Expand Down Expand Up @@ -163,7 +163,6 @@ export function AIChatWindow(props: {
iconOnly
icon="ellipsis"
label={tString(language, 'actions')}
className="!px-2"
variant="blank"
size="default"
/>
Expand All @@ -187,7 +186,6 @@ export function AIChatWindow(props: {
iconOnly
icon="close"
label={tString(language, 'close')}
className="!px-2"
variant="blank"
size="default"
/>
Expand All @@ -202,7 +200,7 @@ export function AIChatWindow(props: {
>
{isEmpty ? (
<div className="flex min-h-full w-full shrink-0 flex-col items-center justify-center gap-6 py-4">
<div className="flex size-32 animate-fadeIn-slow items-center justify-center rounded-full bg-tint-subtle">
<div className="flex size-32 animate-fade-in-slow items-center justify-center rounded-full bg-tint-subtle">
<AIChatIcon
state="intro"
trademark={trademark}
Expand Down Expand Up @@ -231,7 +229,7 @@ export function AIChatWindow(props: {
</div>
<div
ref={inputRef}
className="absolute inset-x-0 bottom-0 mr-2 flex select-none flex-col gap-4 bg-gradient-to-b from-transparent to-50% to-tint-base/9 p-4 pr-2"
className="absolute inset-x-0 bottom-0 mr-2 flex select-none flex-col gap-4 bg-linear-to-b from-transparent to-50% to-tint-base/9 p-4 pr-2"
>
{/* Display an error banner when something went wrong. */}
{chat.error ? <AIChatError chatController={chatController} /> : null}
Expand All @@ -257,7 +255,7 @@ function AIChatError(props: { chatController: AIChatController }) {
const { chatController } = props;

return (
<div className="flex animate-scaleIn flex-wrap justify-between gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md bg-danger px-3 py-2 text-danger text-sm ring-1 ring-danger">
<div className="flex animate-scale-in flex-wrap justify-between gap-2 circular-corners:rounded-2xl rounded-corners:rounded-md bg-danger px-3 py-2 text-danger text-sm ring-1 ring-danger">
<div className="flex items-center gap-2">
<Icon icon="exclamation-triangle" className="size-3.5" />
<span className="flex items-center gap-1">{t(language, 'ai_chat_error')}</span>
Expand All @@ -271,7 +269,7 @@ function AIChatError(props: { chatController: AIChatController }) {
onClick={() => {
chatController.clear();
}}
className="!text-danger hover:bg-danger-5"
className="text-danger! hover:bg-danger-5"
/>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/gitbook/src/components/AIChat/AIChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function AIChatInput(props: {
data-testid="ai-chat-input"
className={tcls(
'resize-none',
'focus:outline-none',
'focus:outline-hidden',
'focus:ring-0',
'w-full',
'px-3',
Expand Down Expand Up @@ -127,7 +127,7 @@ export function AIChatInput(props: {
arrow
>
<div className="flex cursor-help items-center gap-1 circular-corners:rounded-2xl rounded-corners:rounded-md px-2 py-1 text-tint/7 text-xs transition-all hover:bg-tint">
<span className="-ml-1 rounded-corners:rounded circular-corners:rounded-2xl bg-tint-11/7 px-1 py-0.5 font-mono font-semibold text-[0.65rem] text-contrast-tint-11 leading-none">
<span className="-ml-1 circular-corners:rounded-2xl rounded-corners:rounded-sm bg-tint-11/7 px-1 py-0.5 font-mono font-semibold text-[0.65rem] text-contrast-tint-11 leading-none">
{t(language, 'ai_chat_context_badge')}
</span>{' '}
<span>{t(language, 'ai_chat_context_title')}</span>
Expand Down
4 changes: 2 additions & 2 deletions packages/gitbook/src/components/AIChat/AIChatMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function AIChatMessages(props: {
ref={isLastUserMessage ? lastUserMessageRef : undefined}
data-testid="ai-chat-message"
className={tcls(
message.content ? 'animate-fadeIn-slow' : '',
message.content ? 'animate-fade-in-slow' : '',
'shrink-0',
'last:min-h-[calc(100%-5rem)]',
'scroll-mt-36',
Expand All @@ -44,7 +44,7 @@ export function AIChatMessages(props: {
{message.content ? message.content : null}

{isLastMessage && chat.loading ? (
<div className="flex w-full animate-fadeIn-slow flex-wrap gap-2 group-has-[.has-content]/message:hidden">
<div className="flex w-full animate-fade-in-slow flex-wrap gap-2 group-has-[.has-content]/message:hidden">
{Array.from({ length: 7 }).map((_, index) => (
<div
key={index}
Expand Down
10 changes: 5 additions & 5 deletions packages/gitbook/src/components/AIChat/AIResponseFeedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export function AIResponseFeedback(props: {
label={tString(language, 'was_this_helpful_positive_label')}
variant="blank"
className={tcls(
'animate-fadeIn overflow-hidden text-tint-subtle transition-all',
rating !== null && rating !== 1 && 'px-0 text-[0] opacity-0'
'animate-fade-in overflow-hidden text-tint-subtle transition-all',
rating !== null && rating !== 1 && 'px-0 text-[0rem] opacity-0'
)}
size="medium"
style={{ animationDuration: '.5s' }}
Expand All @@ -47,8 +47,8 @@ export function AIResponseFeedback(props: {
label={tString(language, 'was_this_helpful_negative_label')}
variant="blank"
className={tcls(
'animate-fadeIn overflow-hidden text-tint-subtle transition-all',
rating !== null && rating !== -1 && 'px-0 text-[0] opacity-0'
'animate-fade-in overflow-hidden text-tint-subtle transition-all',
rating !== null && rating !== -1 && 'px-0 text-[0rem] opacity-0'
)}
size="medium"
style={{ animationDelay: '.2s', animationDuration: '.5s' }}
Expand All @@ -59,7 +59,7 @@ export function AIResponseFeedback(props: {
/>
{rating !== null ? (
<span
className="ml-2 animate-fadeIn-slow text-tint-subtle"
className="ml-2 animate-fade-in-slow text-tint-subtle"
style={{ animationDelay: '.3s' }}
>
{t(language, 'was_this_helpful_thank_you')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function AIChatFollowupSuggestions(props: {
chatController.postMessage({ message: suggestion });
}}
label={suggestion}
className="!whitespace-normal max-w-full animate-[present_500ms_both] text-left ring-1 ring-tint-subtle"
className="whitespace-normal! max-w-full animate-[present_500ms_both] text-left ring-1 ring-tint-subtle"
size="medium"
variant="blank"
style={{
Expand Down
12 changes: 6 additions & 6 deletions packages/gitbook/src/components/Ads/AdCoverRendering.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export async function AdCoverRendering({
'rounded-lg',
'p-4',
'overflow-hidden',
'shadow-sm'
'shadow-xs'
)}
style={{ backgroundColor: ad.backgroundColor, color: ad.textColor ?? '#ffffff' }}
href={ad.statlink}
Expand All @@ -68,7 +68,7 @@ export async function AdCoverRendering({
}}
/>

<div className={tcls('z-[2]')}>
<div className={tcls('z-2')}>
<img
alt="Large cover"
src={largeImage}
Expand All @@ -81,10 +81,10 @@ export async function AdCoverRendering({
)}
/>
</div>
<div className={tcls('z-[2]')}>
<div className={tcls('z-2')}>
<img alt={ad.company} src={ad.logo} className={tcls('max-w-36', 'max-h-12')} />
</div>
<div className={tcls('flex', 'flex-col', 'z-[2]')}>
<div className={tcls('flex', 'flex-col', 'z-2')}>
<div className={tcls('text-sm', 'font-semibold', 'mb-2')}>{ad.companyTagline}</div>
<div
className={tcls(
Expand All @@ -99,7 +99,7 @@ export async function AdCoverRendering({
{ad.description}
</div>
</div>
<div className={tcls('z-[2]')}>
<div className={tcls('z-2')}>
<span
className={tcls(
'text-sm',
Expand All @@ -119,7 +119,7 @@ export async function AdCoverRendering({
</span>
</div>
<div
className={tcls('absolute', 'inset-0', 'backdrop-blur', 'z-[1]')}
className={tcls('absolute', 'inset-0', 'backdrop-blur-sm', 'z-1')}
style={{
backgroundColor: hexToRgba(ad.backgroundColor, 0.8),
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function AnnouncementBanner(props: {
</Tag>
{closeable ? (
<button
className={`absolute top-0 right-4 mt-2 mr-2 rounded circular-corners:rounded-lg straight-corners:rounded-none p-1.5 transition-all hover:ring-1 sm:right-6 md:right-8 ${style.close}`}
className={`absolute top-0 right-4 mt-2 mr-2 circular-corners:rounded-lg rounded-sm straight-corners:rounded-none p-1.5 transition-all hover:ring-1 sm:right-6 md:right-8 ${style.close}`}
type="button"
onClick={dismissAnnouncement}
title={tString(language, 'close')}
Expand Down Expand Up @@ -130,22 +130,22 @@ const BANNER_STYLES = {
icon: 'circle-exclamation',
iconColor: 'text-warning-subtle',
close: 'hover:bg-tint-base hover:ring-warning-subtle',
link: 'links-default:text-warning links-default:hover:text-warning-strong links-default:decoration-warning/6 links-accent:decoration-warning',
link: 'links-default:text-warning hover:links-default:text-warning-strong links-default:decoration-warning/6 links-accent:decoration-warning',
},
danger: {
container: 'bg-danger decoration-danger/6 ring-danger-subtle',
hover: 'hover:bg-danger-hover',
icon: 'triangle-exclamation',
iconColor: 'text-danger-subtle',
close: 'hover:bg-tint-base hover:ring-danger-subtle',
link: 'links-default:text-danger links-default:hover:text-danger-strong links-default:decoration-danger/6 links-accent:decoration-danger',
link: 'links-default:text-danger hover:links-default:text-danger-strong links-default:decoration-danger/6 links-accent:decoration-danger',
},
success: {
container: 'bg-success decoration-success/6 ring-success-subtle',
hover: 'hover:bg-success-hover',
icon: 'circle-check',
iconColor: 'text-success-subtle',
close: 'hover:bg-tint-base hover:ring-success-subtle',
link: 'links-default:text-success links-default:hover:text-success-strong links-default:decoration-success/6 links-accent:decoration-success',
link: 'links-default:text-success hover:links-default:text-success-strong links-default:decoration-success/6 links-accent:decoration-success',
},
};
6 changes: 3 additions & 3 deletions packages/gitbook/src/components/Cookies/CookiesToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
'fixed',
'z-10',
'bg-tint-base',
'rounded',
'rounded-sm',
'straight-corners:rounded-none',
'circular-corners:rounded-2xl',
'ring-1',
Expand All @@ -61,7 +61,7 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
'text-balance',
'sm:left-auto',
'lg:chat-open:mr-80',
'xl:chat-open:mr-[25rem]',
'xl:chat-open:mr-100',
'transition-all',
'duration-300'
)}
Expand All @@ -88,7 +88,7 @@ export function CookiesToast(props: { privacyPolicy?: string }) {
'flex',
'justify-center',
'items-center',
'rounded-sm',
'rounded-xs',
'circular-corners:rounded-full',
'hover:bg-tint-hover'
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function AnnotationPopover(props: { children: React.ReactNode; body: Reac
'bg-tint',
'ring-1',
'ring-tint',
'rounded',
'rounded-sm',
'shadow-1xs',
'shadow-tint-12/1',
'dark:shadow-tint-1/2',
Expand All @@ -53,7 +53,7 @@ export function AnnotationPopover(props: { children: React.ReactNode; body: Reac
viewBox="0 0 8 5"
className={tcls(
'relative',
'z-[2]',
'z-2',
'fill-tint-3', // Same as bg-tint
'stroke-tint-7', // Same as ring-tint
'[paint-order:stroke_fill]'
Expand Down
2 changes: 1 addition & 1 deletion packages/gitbook/src/components/DocumentView/Blocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
style={[
'mx-auto page-width-wide:mx-0 w-full decoration-primary/6 print:break-inside-avoid',
node.data && 'fullWidth' in node.data && node.data.fullWidth
? 'max-w-screen-2xl'
? 'max-w-screen-xl'
: 'max-w-3xl',
FULL_WIDTH_BLOCKS.includes(node.type) && 'page-width-wide:max-w-screen-2xl',
blockStyle,
Expand Down
4 changes: 2 additions & 2 deletions packages/gitbook/src/components/DocumentView/Caption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ export function Caption(
'overflow-hidden',
'after:block',
'after:absolute',
'after:-inset-[0]',
'after:-inset-0',
'after:pointer-events-none',
fit ? 'w-fit' : null,
withBorder
? 'rounded circular-corners:rounded-2xl straight-corners:rounded-none after:border-tint-subtle after:border after:rounded circular-corners:after:rounded-2xl straight-corners:after:rounded-none dark:after:mix-blend-plus-lighter after:pointer-events-none'
? 'rounded-corners:rounded-sm circular-corners:rounded-2xl after:border-tint-subtle after:border after:rounded circular-corners:after:rounded-2xl rounded-corners:after:rounded-sm dark:after:mix-blend-plus-lighter after:pointer-events-none'
: null,
],
style,
Expand Down
Loading
Loading