Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/tender-buckets-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@react-email/preview-server": patch
---

use tailwindcss v4 in the UI
5 changes: 5 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
}
}
},
"css": {
"parser": {
"tailwindDirectives": true
}
},
"vcs": {
"enabled": true,
"clientKind": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/preview-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@radix-ui/react-tooltip": "1.2.8",
"@react-email/components": "workspace:*",
"@react-email/tailwind": "workspace:2.0.1",
"@tailwindcss/postcss": "4.1.17",
"@types/babel__core": "7.20.5",
"@types/babel__traverse": "7.20.7",
"@types/css-tree": "2.3.10",
Expand All @@ -41,7 +42,6 @@
"@types/react": "19.0.10",
"@types/react-dom": "19.0.4",
"@types/webpack": "5.28.5",
"autoprefixer": "10.4.21",
"clsx": "2.1.1",
"colorjs.io": "0.5.2",
"esbuild": "0.25.10",
Expand All @@ -63,7 +63,7 @@
"source-map-js": "1.2.1",
"stacktrace-parser": "0.1.11",
"tailwind-merge": "3.2.0",
"tailwindcss": "3.4.0",
"tailwindcss": "4.1.17",
"typescript": "5.8.3",
"use-debounce": "10.0.4",
"zod": "4.1.12"
Expand Down
5 changes: 1 addition & 4 deletions packages/preview-server/postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
const path = require('node:path');

module.exports = {
plugins: {
tailwindcss: { config: path.resolve(__dirname, 'tailwind.config.ts') },
autoprefixer: {},
'@tailwindcss/postcss': {},
},
};
127 changes: 124 additions & 3 deletions packages/preview-server/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,127 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";

@theme {
--background-image-gradient: linear-gradient(
145.37deg,
rgba(255, 255, 255, 0.09) -8.75%,
rgba(255, 255, 255, 0.027) 83.95%
);
--background-image-gradient-hover: linear-gradient(
145.37deg,
rgba(255, 255, 255, 0.1) -8.75%,
rgba(255, 255, 255, 0.057) 83.95%
);
--background-image-shine: linear-gradient(
45deg,
rgba(255, 255, 255, 0) 45%,
rgba(255, 255, 255, 1) 50%,
rgba(255, 255, 255, 0) 55%,
rgba(255, 255, 255, 0) 100%
);

--color-cyan-1: #0091f70a;
--color-cyan-2: #02a7f211;
--color-cyan-3: #00befd28;
--color-cyan-4: #00baff3b;
--color-cyan-5: #00befd4d;
--color-cyan-6: #00c7fd5e;
--color-cyan-7: #14cdff75;
--color-cyan-8: #11cfff95;
--color-cyan-9: #00cfffc3;
--color-cyan-10: #28d6ffcd;
--color-cyan-11: #52e1fee5;
--color-cyan-12: #bbf3fef7;

--color-slate-1: #00000000;
--color-slate-2: #d8f4f609;
--color-slate-3: #ddeaf814;
--color-slate-4: #d3edf81d;
--color-slate-5: #d9edfe25;
--color-slate-6: #d6ebfd30;
--color-slate-7: #d9edff40;
--color-slate-8: #d9edff5d;
--color-slate-9: #dfebfd6d;
--color-slate-10: #e5edfd7b;
--color-slate-11: #f1f7feb5;
--color-slate-12: #fcfdffef;

--font-sans:
var(--font-inter), ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-mono:
var(--font-sf-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;

--animate-spin-slow: spin 3s linear infinite;
--animate-spin-fast: spin 1.5s linear infinite;

@keyframes shine {
0% {
background-position: -100%;
}

100% {
background-position: 100%;
}
}

@keyframes dash {
0% {
stroke-dashoffset: 1000;
}

100% {
stroke-dashoffset: 0;
}
}

@keyframes spin {
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(360deg);
}
}
}

@utility arrow-hide {
appearance: textfield;

&::-webkit-inner-spin-button {
appearance: none;
margin: 0px;
}

&::-webkit-outer-spin-button {
appearance: none;
margin: 0px;
}
}

/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.

If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}

button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}

html {
color-scheme: dark;
Expand Down
2 changes: 1 addition & 1 deletion packages/preview-server/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default async function RootLayout({
lang="en"
>
<body className="relative h-screen bg-black text-slate-11 leading-loose selection:bg-cyan-5 selection:text-cyan-12">
<div className="bg-gradient-to-t from-slate-3 flex flex-col">
<div className="bg-linear-to-t from-slate-3 flex flex-col">
<EmailsProvider
initialEmailsDirectoryMetadata={emailsDirectoryMetadata}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ export const ErrorOverlay = ({ error }: ErrorOverlayProps) => {
min-h-[50vh] w-full max-w-lg sm:rounded-lg md:max-w-[568px] lg:max-w-[920px]
absolute left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]
rounded-t-sm overflow-hidden bg-white text-black shadow-lg duration-200
flex flex-col selection:!text-black
flex flex-col selection:text-black!
"
>
<div className="bg-red-500 h-3" />
<div className="flex flex-grow p-6 min-w-0 max-w-full flex-col space-y-1.5">
<div className="flex-shrink pb-2 text-xl tracking-tight">
<div className="flex grow p-6 min-w-0 max-w-full flex-col space-y-1.5">
<div className="shrink pb-2 text-xl tracking-tight">
<b>{error.name}</b>: <Message>{error.message}</Message>
</div>
{error.stack ? (
<div className="flex-grow scroll-px-4 overflow-x-auto rounded-lg bg-black p-2 text-gray-100">
<pre className="w-full min-w-0 font-mono leading-6 selection:!text-cyan-12 text-xs">
<div className="grow scroll-px-4 overflow-x-auto rounded-lg bg-black p-2 text-gray-100">
<pre className="w-full min-w-0 font-mono leading-6 selection:text-cyan-12! text-xs">
{error.stack}
</pre>
</div>
Expand Down
6 changes: 3 additions & 3 deletions packages/preview-server/src/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ const getAppearance = (appearance: Appearance | undefined) => {
return [
'border-white bg-white text-black transition-colors duration-200 ease-in-out',
'hover:bg-white/90',
'focus:bg-white/90 focus:outline-none focus:ring-2 focus:ring-white/20',
'focus:bg-white/90 focus:outline-hidden focus:ring-2 focus:ring-white/20',
'mt-2 mb-2 aria-disabled:border-transparent aria-disabled:bg-slate-11',
];
case 'gradient':
return [
'bg-gradient border-[#34343A] backdrop-blur-[1.25rem]',
'hover:bg-gradientHover',
'focus:bg-gradientHover focus:outline-none focus:ring-2 focus:ring-white/20',
'hover:bg-gradient-hover',
'focus:bg-gradient-hover focus:outline-hidden focus:ring-2 focus:ring-white/20',
];
default:
unreachable(appearance);
Expand Down
4 changes: 2 additions & 2 deletions packages/preview-server/src/components/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ export const Code: React.FC<Readonly<CodeProps>> = ({
scroll={false}
aria-selected={isHighlighted}
className={cn(
'text-[#49494f] relative text-[13px] font-light font-[MonoLisa,_Menlo,_monospace] align-middle scroll-mt-[325px] select-none pr-3 cursor-pointer hover:text-slate-12 transition-colors',
'aria-selected:text-cyan-11 aria-selected:hover:text-cyan-11 aria-selected:bg-cyan-5 [&+*]:aria-selected:bg-cyan-5',
'text-[#49494f] relative text-[13px] font-light font-[MonoLisa,Menlo,monospace] align-middle scroll-mt-[325px] select-none pr-3 cursor-pointer hover:text-slate-12 transition-colors',
'aria-selected:text-cyan-11 aria-selected:hover:text-cyan-11 aria-selected:bg-cyan-5 aria-selected:[&+*]:bg-cyan-5',
isHighlightStart && 'rounded-tl-sm',
isHighlightEnd && 'rounded-bl-sm',
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const IconButton = React.forwardRef<
type="button"
{...props}
className={cn(
'focus:ring-gray-8 rounded text-slate-11 transition duration-200 ease-in-out hover:text-slate-12 focus:text-slate-12 focus:outline-none focus:ring-2',
'focus:ring-gray-8 rounded-sm text-slate-11 transition duration-200 ease-in-out hover:text-slate-12 focus:text-slate-12 focus:outline-hidden focus:ring-2',
className,
)}
ref={forwardedRef}
Expand Down
6 changes: 3 additions & 3 deletions packages/preview-server/src/components/send.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const Send = ({ markup }: { markup: string }) => {
>
<Popover.Trigger asChild>
<button
className="box-border flex h-5 w-20 items-center justify-center self-center rounded-lg border border-slate-6 bg-slate-2 px-4 py-4 text-center font-sans text-sm text-slate-11 outline-none transition duration-300 ease-in-out hover:border-slate-10 hover:text-slate-12"
className="box-border flex h-5 w-20 items-center justify-center self-center rounded-lg border border-slate-6 bg-slate-2 px-4 py-4 text-center font-sans text-sm text-slate-11 outline-hidden transition duration-300 ease-in-out hover:border-slate-10 hover:text-slate-12"
type="submit"
>
Send
Expand All @@ -82,7 +82,7 @@ export const Send = ({ markup }: { markup: string }) => {
</label>
<input
autoFocus
className="mb-3 w-full appearance-none rounded-lg border border-slate-6 bg-slate-3 px-2 py-1 text-sm text-slate-12 placeholder-slate-10 outline-none transition duration-300 ease-in-out focus:ring-1 focus:ring-slate-10"
className="mb-3 w-full appearance-none rounded-lg border border-slate-6 bg-slate-3 px-2 py-1 text-sm text-slate-12 placeholder-slate-10 outline-hidden transition duration-300 ease-in-out focus:ring-1 focus:ring-slate-10"
defaultValue={to}
id={toId}
onChange={(e) => {
Expand All @@ -99,7 +99,7 @@ export const Send = ({ markup }: { markup: string }) => {
Subject
</label>
<input
className="mb-3 w-full appearance-none rounded-lg border border-slate-6 bg-slate-3 px-2 py-1 text-sm text-slate-12 placeholder-slate-10 outline-none transition duration-300 ease-in-out focus:ring-1 focus:ring-slate-10"
className="mb-3 w-full appearance-none rounded-lg border border-slate-6 bg-slate-3 px-2 py-1 text-sm text-slate-12 placeholder-slate-10 outline-hidden transition duration-300 ease-in-out focus:ring-1 focus:ring-slate-10"
defaultValue={subject}
id={subjectId}
onChange={(e) => {
Expand Down
12 changes: 6 additions & 6 deletions packages/preview-server/src/components/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ export const Shell = ({ children, currentEmailOpenSlug }: ShellProps) => {
>
<div
className={
'flex h-[4.375rem] items-center justify-between border-slate-6 border-b px-6 lg:hidden'
'flex h-17.5 items-center justify-between border-slate-6 border-b px-6 lg:hidden'
}
>
<div className="flex h-[4.375rem] items-center">
<div className="flex h-17.5 items-center">
<Logo />
</div>
<button
className="flex h-6 w-6 items-center justify-center rounded text-white"
className="flex h-6 w-6 items-center justify-center rounded-sm text-white"
onClick={() => {
setSidebarToggled((v) => !v);
}}
Expand All @@ -62,11 +62,11 @@ export const Shell = ({ children, currentEmailOpenSlug }: ShellProps) => {
</svg>
</button>
</div>
<div className="w-[100dvw] flex h-[calc(100dvh-4.375rem)] lg:h-[100dvh]">
<div className="w-dvw flex h-[calc(100dvh-4.375rem)] lg:h-dvh">
<React.Suspense>
<Sidebar
className={cn(
'fixed top-[4.375rem] left-0 z-[9999] h-full max-h-full w-full max-w-full will-change-auto [transition:width_0.2s_ease-in-out]',
'fixed top-17.5 left-0 z-9999 h-full max-h-full w-full max-w-full will-change-auto [transition:width_0.2s_ease-in-out]',
'lg:static lg:inline-block lg:z-auto lg:max-h-full lg:w-[16rem]',
{
'-translate-x-full lg:translate-x-0': sidebarToggled,
Expand All @@ -80,7 +80,7 @@ export const Shell = ({ children, currentEmailOpenSlug }: ShellProps) => {
className={cn(
'inline-block relative overflow-hidden will-change-[width]',
'w-full h-full',
'[transition:width_0.2s_ease-in-out,_transform_0.2s_ease-in-out]',
'[transition:width_0.2s_ease-in-out,transform_0.2s_ease-in-out]',
{
'lg:w-[calc(100%-16rem)]': sidebarToggled,
'opacity-0 lg:opacity-100': !sidebarToggled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const FileTreeDirectoryChildren = (props: {
>
{props.isRoot ? null : (
<motion.div
className="absolute top-1 left-[0.4rem] inset-0 h-6 w-px rounded-sm bg-cyan-11"
className="absolute top-1 left-[0.4rem] inset-0 h-6 w-px rounded-xs bg-cyan-11"
layoutId="active-file"
transition={{
type: 'spring',
Expand Down
4 changes: 2 additions & 2 deletions packages/preview-server/src/components/sidebar/file-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export const FileTree = ({
emailsDirectoryMetadata,
}: FileTreeProps) => {
return (
<div className="flex w-full h-full flex-col lg:w-full lg:min-w-[14.5rem]">
<nav className="flex flex-grow flex-col p-4 pr-0 pl-0">
<div className="flex w-full h-full flex-col lg:w-full lg:min-w-58">
<nav className="flex grow flex-col p-4 pr-0 pl-0">
<Collapsible.Root open>
<React.Suspense>
<FileTreeDirectoryChildren
Expand Down
2 changes: 1 addition & 1 deletion packages/preview-server/src/components/sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const Sidebar = ({ className, currentEmailOpenSlug }: SidebarProps) => {
<div className="flex w-full h-full overflow-hidden flex-col border-slate-6 border-r">
<div
className={clsx(
'hidden min-h-14 flex-shrink items-center py-2 px-3 lg:flex border-b border-slate-4',
'hidden min-h-14 shrink items-center py-2 px-3 lg:flex border-b border-slate-4',
)}
>
<h2>
Expand Down
Loading
Loading