Skip to content
Open
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
6 changes: 6 additions & 0 deletions .server-changes/tailwind-v4-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: improvement
---

Migrated the webapp to Tailwind CSS v4 with a CSS-first `@theme`. Semantic color tokens are now CSS variables overridable per theme (multi-theme support). Tailwind runs through `@tailwindcss/postcss`; plugins replaced or upgraded to v4-compatible versions.
6 changes: 6 additions & 0 deletions .server-changes/theme-color-tokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
area: webapp
type: improvement
---

Replaced raw charcoal-* Tailwind classes with semantic theme tokens (surfaces, borders, text) across the dashboard. Adds surface/border/text tokens to the themable layer so future themes only override CSS variables.
16 changes: 8 additions & 8 deletions apps/webapp/app/components/AskAI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ function AskAIDialog({ initialQuery, isOpen, onOpenChange, closeAskAI }: AskAIDi
return (
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
<DialogContent className="animated-gradient-glow flex max-h-[90vh] min-h-fit w-full flex-col justify-between gap-0 px-0 pb-0 pt-0 sm:max-w-prose">
<DialogHeader className="flex h-[2.75rem] items-start justify-center rounded-t-md bg-background-bright pl-3">
<DialogHeader className="flex h-11 items-start justify-center rounded-t-md bg-background-bright pl-3">
<div className="flex items-center gap-1">
<AISparkleIcon className="size-5" />
<DialogTitle className="text-sm font-medium text-text-bright">Ask AI</DialogTitle>
Expand Down Expand Up @@ -226,7 +226,7 @@ function ChatMessages({
];

return (
<div className="flex-1 overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div className="flex-1 overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control">
{conversation.length === 0 ? (
<motion.div
className="flex flex-col gap-2 pb-2"
Expand All @@ -249,7 +249,7 @@ function ChatMessages({
{exampleQuestions.map((question, index) => (
<motion.button
key={index}
className="group flex w-fit items-center gap-2 rounded-full border border-dashed border-charcoal-600 px-4 py-2 transition-colors hover:border-solid hover:border-indigo-500"
className="group flex w-fit items-center gap-2 rounded-full border border-dashed border-border-bright px-4 py-2 transition-colors hover:border-solid hover:border-indigo-500"
onClick={() => onExampleClick(question)}
variants={{
hidden: {
Expand Down Expand Up @@ -468,7 +468,7 @@ function ChatInterface({ initialQuery }: { initialQuery?: string }) {
error={error}
addFeedback={addFeedback}
/>
<form onSubmit={handleSubmit} className="flex-shrink-0 border-t border-grid-bright p-4">
<form onSubmit={handleSubmit} className="shrink-0 border-t border-grid-bright p-4">
<div className="flex gap-3">
<input
type="text"
Expand Down Expand Up @@ -511,7 +511,7 @@ function ChatInterface({ initialQuery }: { initialQuery?: string }) {
disabled={!message.trim()}
LeadingIcon={<ArrowUpIcon className="size-5 text-text-bright" />}
variant="primary/large"
className="size-10 min-w-10 rounded-full group-disabled/button:border-charcoal-550 group-disabled/button:bg-charcoal-600"
className="size-10 min-w-10 rounded-full group-disabled/button:border-border-brighter group-disabled/button:bg-surface-control"
/>
)}
</div>
Expand All @@ -531,11 +531,11 @@ function GradientSpinnerBackground({
}) {
return (
<div
className={`flex rounded-full bg-gradient-to-br from-indigo-500 via-purple-500 to-fuchsia-500 p-px ${className}`}
className={`flex rounded-full bg-linear-to-br from-indigo-500 via-purple-500 to-fuchsia-500 p-px ${className}`}
>
<div
className={`flex h-full w-full items-center justify-center rounded-full bg-charcoal-600 ${
hoverEffect ? "transition group-hover:bg-charcoal-550" : ""
className={`flex h-full w-full items-center justify-center rounded-full bg-surface-control ${
hoverEffect ? "transition group-hover:bg-surface-control-hover" : ""
}`}
>
{children}
Expand Down
6 changes: 3 additions & 3 deletions apps/webapp/app/components/BackgroundWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function BackgroundWrapper({ children }: { children: ReactNode }) {
return (
<div className="relative h-full w-full overflow-hidden bg-background-dimmed lg:bg-transparent">
<div
className="absolute left-0 top-0 hidden w-[260px] bg-contain bg-left-top bg-no-repeat lg:block"
className="absolute left-0 top-0 hidden w-[260px] bg-contain bg-top-left bg-no-repeat lg:block"
style={{
backgroundImage: `url(${blurredDashboardBackgroundMenuTop})`,
aspectRatio: "auto",
Expand All @@ -17,7 +17,7 @@ export function BackgroundWrapper({ children }: { children: ReactNode }) {
/>

<div
className="absolute bottom-0 left-0 hidden w-[260px] bg-contain bg-left-bottom bg-no-repeat lg:block"
className="absolute bottom-0 left-0 hidden w-[260px] bg-contain bg-bottom-left bg-no-repeat lg:block"
style={{
backgroundImage: `url(${blurredDashboardBackgroundMenuBottom})`,
aspectRatio: "auto",
Expand All @@ -27,7 +27,7 @@ export function BackgroundWrapper({ children }: { children: ReactNode }) {
/>

<div
className="absolute top-0 hidden bg-left-top bg-no-repeat lg:block"
className="absolute top-0 hidden bg-top-left bg-no-repeat lg:block"
style={{
left: "260px",
backgroundImage: `url(${blurredDashboardBackgroundTable})`,
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/BlankStatePanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ export function DeploymentsNoneDev() {
organization={organization}
project={project}
environment={environment}
className="w-fit border border-charcoal-600 bg-secondary hover:border-charcoal-550 hover:bg-charcoal-600"
className="w-fit border border-border-bright bg-secondary hover:border-border-brighter hover:bg-surface-control"
/>
</StepContentContainer>
</>
Expand Down
4 changes: 2 additions & 2 deletions apps/webapp/app/components/DefinitionTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ export function DefinitionTip({
<TooltipProvider>
<Tooltip disableHoverableContent={disableHoverableContent}>
<TooltipTrigger className="text-left">
<span className="cursor-default underline decoration-charcoal-500 decoration-dashed underline-offset-4 transition hover:decoration-charcoal-400">
<span className="cursor-default underline decoration-text-faint decoration-dashed underline-offset-4 transition hover:decoration-text-dimmed">
{children}
</span>
</TooltipTrigger>
<TooltipContent align="end" side="right" className="w-[16rem] min-w-[16rem]">
<TooltipContent align="end" side="right" className="w-64 min-w-64">
<Header3 className="mb-1">{title}</Header3>
{typeof content === "string" ? (
<Paragraph variant="small">{content}</Paragraph>
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/Feedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function Feedback({ button, defaultValue = "bug", onOpenChange }: Feedbac
<DialogHeader>Contact us</DialogHeader>
<div className="mt-2 flex flex-col gap-4">
<div className="flex items-center gap-4">
<Icon icon={EnvelopeIcon} className="size-10 min-w-[2.5rem] text-blue-500" />
<Icon icon={EnvelopeIcon} className="size-10 min-w-10 text-blue-500" />
<Paragraph variant="base/bright">
How can we help? We read every message and will respond as quickly as we can.
</Paragraph>
Expand Down
6 changes: 3 additions & 3 deletions apps/webapp/app/components/GitMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function GitMetadataBranch({
leadingIconClassName="group-hover/table-row:text-text-bright"
iconSpacing="gap-x-1"
to={git.branchUrl}
className="pl-1 duration-0 [&_span]:duration-0 [&_span]:group-hover/table-row:text-text-bright"
className="pl-1 duration-0 [&_span]:duration-0 group-hover/table-row:[&_span]:text-text-bright"
>
{git.branchName}
</LinkButton>
Expand All @@ -52,7 +52,7 @@ export function GitMetadataCommit({
LeadingIcon={<GitCommitIcon className="size-4" />}
leadingIconClassName="group-hover/table-row:text-text-bright"
iconSpacing="gap-x-1"
className="pl-1 duration-0 [&_span]:duration-0 [&_span]:group-hover/table-row:text-text-bright"
className="pl-1 duration-0 [&_span]:duration-0 group-hover/table-row:[&_span]:text-text-bright"
>
{`${git.shortSha} / ${git.commitMessage}`}
</LinkButton>
Expand All @@ -78,7 +78,7 @@ export function GitMetadataPullRequest({
LeadingIcon={<GitPullRequestIcon className="size-4" />}
leadingIconClassName="group-hover/table-row:text-text-bright"
iconSpacing="gap-x-1"
className="pl-1 duration-0 [&_span]:duration-0 [&_span]:group-hover/table-row:text-text-bright"
className="pl-1 duration-0 [&_span]:duration-0 group-hover/table-row:[&_span]:text-text-bright"
>
#{git.pullRequestNumber} {git.pullRequestTitle}
</LinkButton>
Expand Down
6 changes: 3 additions & 3 deletions apps/webapp/app/components/ListPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function ListPagination({ list, className }: { list: List; className?: st
<NextButton cursor={list.pagination.next} />
<div
className={cn(
"order-2 h-6 w-px bg-charcoal-600 transition-colors peer-hover/next:bg-charcoal-550 peer-hover/prev:bg-charcoal-550",
"order-2 h-6 w-px bg-surface-control transition-colors peer-hover/next:bg-surface-control-hover peer-hover/prev:bg-surface-control-hover",
bothDisabled && "opacity-30"
)}
/>
Expand All @@ -41,7 +41,7 @@ function PreviousButton({ cursor }: { cursor?: string }) {
variant={"secondary/small"}
LeadingIcon={ChevronLeftIcon}
className={cn(
"flex items-center rounded-r-none border-r-0 pl-2 pr-[0.5625rem]",
"flex items-center rounded-r-none border-r-0 pl-2 pr-2.25",
!path && "cursor-not-allowed opacity-50"
)}
onClick={(e) => !path && e.preventDefault()}
Expand All @@ -63,7 +63,7 @@ function NextButton({ cursor }: { cursor?: string }) {
variant={"secondary/small"}
TrailingIcon={ChevronRightIcon}
className={cn(
"flex items-center rounded-l-none border-l-0 pl-[0.5625rem] pr-2",
"flex items-center rounded-l-none border-l-0 pl-2.25 pr-2",
!path && "cursor-not-allowed opacity-50"
)}
onClick={(e) => !path && e.preventDefault()}
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/LoginPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) {
</div>
<div className="flex flex-col items-center gap-4 px-8">
<Paragraph>Trusted by developers at</Paragraph>
<div className="flex w-full flex-wrap items-center justify-center gap-x-6 gap-y-3 text-charcoal-500 xl:justify-between xl:gap-0">
<div className="flex w-full flex-wrap items-center justify-center gap-x-6 gap-y-3 text-text-faint xl:justify-between xl:gap-0">
<LyftLogo className="w-11" />
<UnkeyLogo />
<MiddayLogo />
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/ProductHuntBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function ProductHuntBanner() {
<LinkButton
to="https://www.producthunt.com/posts/trigger-dev"
target="_blank"
className="!text-white underline underline-offset-2 transition hover:decoration-charcoal-100 hover:decoration-2"
className="text-white! underline underline-offset-2 transition hover:decoration-text-bright hover:decoration-2"
variant="tertiary/small"
>
Vote for us today only!
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/UserProfilePhoto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export function UserAvatar({
/>
</div>
) : (
<UserCircleIcon className={cn("aspect-square text-charcoal-400", className)} />
<UserCircleIcon className={cn("aspect-square text-text-dimmed", className)} />
);
}
6 changes: 3 additions & 3 deletions apps/webapp/app/components/WarmStarts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function WarmStartIconWithTooltip({
}) {
return (
<SimpleTooltip
className="relative z-[9999]"
className="relative z-9999"
button={<WarmStartIcon isWarmStart={isWarmStart} className={className} />}
content={<WarmStartTooltipContent />}
/>
Expand All @@ -42,14 +42,14 @@ function WarmStartTooltipContent() {
<div className="flex max-w-xs flex-col gap-4 p-1">
<div>
<WarmStartCombo isWarmStart={false} className="mb-0.5 text-text-bright" />
<Paragraph variant="small" className="!text-wrap text-text-dimmed">
<Paragraph variant="small" className="text-wrap! text-text-dimmed">
A cold start happens when we need to boot up a new machine for your run to execute. This
takes longer than a warm start.
</Paragraph>
</div>
<div>
<WarmStartCombo isWarmStart={true} className="mb-0.5 text-text-bright" />
<Paragraph variant="small" className="!text-wrap text-text-dimmed">
<Paragraph variant="small" className="text-wrap! text-text-dimmed">
A warm start happens when we can reuse a machine from a run that recently finished. This
takes less time than a cold start.
</Paragraph>
Expand Down
12 changes: 6 additions & 6 deletions apps/webapp/app/components/admin/FeatureFlagsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,14 @@ export function FeatureFlagsDialog({
return (
<div
key={key}
className="flex items-center justify-between rounded-md border border-transparent bg-charcoal-750 px-3 py-2.5"
className="flex items-center justify-between rounded-md border border-transparent bg-background-hover px-3 py-2.5"
title="Global-level setting - not editable per org"
>
<div className="min-w-0 flex-1">
<div className="truncate text-sm text-text-dimmed">{key}</div>
<div className="text-xs text-charcoal-400">global: {globalDisplay}</div>
<div className="text-xs text-text-dimmed">global: {globalDisplay}</div>
</div>
<LockClosedIcon className="size-4 text-charcoal-500" />
<LockClosedIcon className="size-4 text-text-faint" />
</div>
);
}
Expand All @@ -187,7 +187,7 @@ export function FeatureFlagsDialog({
"flex items-center justify-between rounded-md border px-3 py-2.5",
isOverridden
? "border-indigo-500/20 bg-indigo-500/5"
: "border-transparent bg-charcoal-750"
: "border-transparent bg-background-hover"
)}
>
<div className="min-w-0 flex-1">
Expand All @@ -199,7 +199,7 @@ export function FeatureFlagsDialog({
>
{key}
</div>
<div className="text-xs text-charcoal-400">global: {globalDisplay}</div>
<div className="text-xs text-text-dimmed">global: {globalDisplay}</div>
</div>

<div className="flex items-center gap-2">
Expand Down Expand Up @@ -283,7 +283,7 @@ export function FeatureFlagsDialog({
<summary className="cursor-pointer text-xs text-text-dimmed hover:text-text-bright">
Preview JSON
</summary>
<pre className="mt-1 max-h-40 overflow-auto rounded bg-charcoal-800 p-2 text-xs text-text-dimmed">
<pre className="mt-1 max-h-40 overflow-auto rounded bg-background-bright p-2 text-xs text-text-dimmed">
{jsonPreview}
</pre>
</details>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function MaxProjectsSection({
}, [savedJustNow, hasFieldErrors]);

return (
<section className="flex flex-col gap-3 rounded-md border border-charcoal-700 bg-charcoal-800 p-4">
<section className="flex flex-col gap-3 rounded-md border border-grid-bright bg-background-bright p-4">
<div className="flex items-center justify-between">
<Header2>Maximum projects</Header2>
{!isEditing && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function RateLimitSection({
};

return (
<section className="flex flex-col gap-3 rounded-md border border-charcoal-700 bg-charcoal-800 p-4">
<section className="flex flex-col gap-3 rounded-md border border-grid-bright bg-background-bright p-4">
<div className="flex items-center justify-between">
<Header2>{title}</Header2>
{!isEditing && (
Expand Down
10 changes: 8 additions & 2 deletions apps/webapp/app/components/billing/FreePlanUsage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ export function FreePlanUsage({ to, percentage }: { to: string; percentage: numb
const color = useTransform(
widthProgress,
[0, 74, 75, 95, 100],
["#22C55E", "#22C55E", "#F59E0B", "#F43F5E", "#F43F5E"]
[
"var(--color-success)",
"var(--color-success)",
"var(--color-warning)",
"var(--color-error)",
"var(--color-error)",
]
Comment on lines +12 to +18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 CSS variables in framer-motion color interpolation may not animate smoothly

The useTransform hook from framer-motion at apps/webapp/app/components/billing/FreePlanUsage.tsx:9-18 was changed from hex color strings (#22C55E, #F59E0B, #F43F5E) to CSS variable references (var(--color-success), var(--color-warning), var(--color-error)). Framer-motion's useTransform interpolates between output values by parsing color strings into numeric components. CSS variable references like var(--color-success) are opaque strings that framer-motion cannot parse or interpolate — the color will likely snap between values at the breakpoints rather than smoothly transitioning. Given the breakpoints are [0, 74, 75, 95, 100] with duplicate colors at the edges, the interpolation window is very narrow (1 unit at 74→75, 5 units at 95→100), so the visual impact may be minimal, but the behavior has changed from smooth interpolation to discrete snapping.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

);

const hasHitLimit = cappedPercentage >= 1;

return (
<div
className={cn(
"rounded border border-charcoal-700 bg-charcoal-750 p-2.5",
"rounded border border-grid-bright bg-background-hover p-2.5",
hasHitLimit && "border-error/40"
)}
>
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/billing/UsageBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function Legend({ text, value, position, percentage, tooltipContent }: LegendPro
return (
<div
className={cn(
"absolute left-full z-10 flex border-charcoal-500",
"absolute left-full z-10 flex border-border-brightest",
positions[position],
flipLegendPosition === true ? "-translate-x-full border-r" : "border-l"
)}
Expand Down
6 changes: 3 additions & 3 deletions apps/webapp/app/components/code/AIQueryInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export function AIQueryInput({
onChange={(e) => setPrompt(e.target.value)}
disabled={isLoading}
rows={8}
className="m-0 min-h-10 w-full resize-none border-0 bg-background-bright px-3 py-2.5 text-sm text-text-bright scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600 file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-text-dimmed focus:border-0 focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50"
className="m-0 min-h-10 w-full resize-none border-0 bg-background-bright px-3 py-2.5 text-sm text-text-bright scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control file:border-0 file:bg-transparent file:text-base file:font-medium placeholder:text-text-dimmed focus:border-0 focus:outline-hidden focus:ring-0 focus-visible:outline-hidden focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50"
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey && prompt.trim() && !isLoading) {
e.preventDefault();
Expand Down Expand Up @@ -342,7 +342,7 @@ export function AIQueryInput({
className="overflow-hidden"
>
<div className="px-1">
<div className="rounded-b-lg border-x border-b border-grid-dimmed bg-charcoal-850 p-3 pb-1">
<div className="rounded-b-lg border-x border-b border-grid-dimmed bg-background-dimmed p-3 pb-1">
<div className="mb-1 flex items-center justify-between">
<div className="flex items-center gap-1">
{isLoading ? (
Expand Down Expand Up @@ -390,7 +390,7 @@ export function AIQueryInput({
</Button>
)}
</div>
<div className="streamdown-container max-h-96 overflow-y-auto text-xs text-text-dimmed scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div className="streamdown-container max-h-96 overflow-y-auto text-xs text-text-dimmed scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control">
<Suspense fallback={<p className="whitespace-pre-wrap">{thinking}</p>}>
<StreamdownRenderer isAnimating={isLoading}>{thinking}</StreamdownRenderer>
</Suspense>
Expand Down
Loading
Loading