feat(ui): establish premium design foundation from mockups
Extract design tokens from the mockup set (F:\Pictures\website\jobtracker\new
dashboard, pipeline, job-workspace, features, workflow screens) into the
central theme so every screen picks the change up automatically:
- Heading weight: h1-h4 go bold/black (800/700) to match the mockups' heavy
display type; h5/h6 stay a lighter semibold so dense screens don't turn
into a wall of black text.
- Card shadow: replace the flat 1px "section" shadow + visible border with a
soft floating shadow and no border, matching how mockup cards sit on the
grey page background.
- Border radius: 10/12/8px -> 14/16/10px across shape/card/button defaults,
matching the mockups' rounder corners.
- New GradientButton component wrapping the mockup's signature indigo->cyan
CTA gradient ("Tailor my CV for this role", "See the interface tour"),
reserved for the single most important AI-assist/hero action per screen.
The dark navy sidebar (#0f172a) already matched the mockups from an earlier
pass -- untouched here.
Verified: tsc clean, full frontend suite green (65/65). Live visual
screenshot verification wasn't possible -- the Browser pane's screenshot
tool times out in this environment; verified structurally via read_page
and the app rendering without console errors instead.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
|
||||
import Button, { ButtonProps } from "@mui/material/Button";
|
||||
|
||||
import { GRADIENT_CTA } from "../theme";
|
||||
|
||||
/**
|
||||
* The mockup's gradient AI/primary-action pill ("Tailor my CV for this role", "See the
|
||||
* interface tour"). Reserve this for the single most important AI-assist or hero action on a
|
||||
* screen -- everything else stays a plain contained/outlined Button, or the gradient stops
|
||||
* reading as a signal and becomes wallpaper.
|
||||
*/
|
||||
export default function GradientButton({ sx, disabled, ...props }: ButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
disabled={disabled}
|
||||
sx={[
|
||||
(theme: any) => ({
|
||||
background: disabled ? undefined : GRADIENT_CTA,
|
||||
color: disabled ? undefined : "#fff",
|
||||
boxShadow: disabled ? undefined : theme.vars.customShadows.gradientButton,
|
||||
"&:hover": disabled
|
||||
? undefined
|
||||
: { background: GRADIENT_CTA, filter: "brightness(1.06)", boxShadow: theme.vars.customShadows.gradientButton },
|
||||
}),
|
||||
...(Array.isArray(sx) ? sx : sx ? [sx] : []),
|
||||
]}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
+37
-15
@@ -173,30 +173,49 @@ function buildCustomShadows(palette: any) {
|
||||
const primaryColor = palette.primary.main;
|
||||
return {
|
||||
button: `0px 0.711px 1.422px 0px ${alpha(shadowColor, 0.05)}`,
|
||||
section: `0px 1px 2px 0px ${alpha(shadowColor, 0.07)}`,
|
||||
// Mockup cards float on the grey page background with no visible border, just a soft,
|
||||
// fairly generous shadow (dashboard stat tiles, pipeline columns' cards, job-workspace
|
||||
// panels) -- the old 1px "section" shadow read as flat/bordered rather than elevated.
|
||||
section: `0px 1px 2px 0px ${alpha(shadowColor, 0.04)}, 0px 8px 24px -12px ${alpha(shadowColor, 0.12)}`,
|
||||
tooltip: `0px 12px 16px -4px ${alpha(shadowColor, 0.08)}, 0px 4px 6px -2px ${alpha(shadowColor, 0.03)}`,
|
||||
focus: `0px 0px 0px 3px ${alpha(primaryColor, 0.2)}`,
|
||||
// Gradient CTA (mockup's "Tailor my CV" / "See the interface tour" buttons) gets its own
|
||||
// colored glow instead of the flat neutral button shadow.
|
||||
gradientButton: `0px 4px 14px -4px ${alpha(primaryColor, 0.45)}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Mockup's signature AI/primary-action gradient (dashboard "Tailor my CV", landing "See the
|
||||
// interface tour"). Exported so every gradient CTA across the app stays pixel-identical
|
||||
// instead of each screen inventing its own two-color mix -- consumed via <GradientButton>
|
||||
// (src/components/GradientButton.tsx), not applied as a MUI Button variant (MUI's variant
|
||||
// override machinery needs TS module augmentation for a genuinely new variant name; a plain
|
||||
// wrapper component is far less code for the same result).
|
||||
export const GRADIENT_CTA = "linear-gradient(90deg, #6366F1 0%, #22D3EE 100%)";
|
||||
|
||||
function buildTypography() {
|
||||
return {
|
||||
fontFamily: "'Archivo', system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif",
|
||||
letterSpacing: 0,
|
||||
h1: { fontWeight: 500, fontSize: 36, lineHeight: "40px" },
|
||||
h2: { fontWeight: 500, fontSize: 30, lineHeight: "34px" },
|
||||
h3: { fontWeight: 500, fontSize: 26, lineHeight: "30px" },
|
||||
h4: { fontWeight: 500, fontSize: 22, lineHeight: "26px" },
|
||||
h5: { fontWeight: 500, fontSize: 18, lineHeight: "22px" },
|
||||
h6: { fontWeight: 500, fontSize: 16, lineHeight: "20px" },
|
||||
subtitle1: { fontWeight: 500, fontSize: 14, lineHeight: "18px" },
|
||||
subtitle2: { fontWeight: 500, fontSize: 13, lineHeight: "17px" },
|
||||
// Mockup headings (F:\Pictures\website\jobtracker\new) are bold/black-weight display
|
||||
// type ("Dashboard", "One workspace for the whole search") -- 500 across the board read
|
||||
// as noticeably lighter/flatter than that reference. h1-h4 (page titles, hero, section
|
||||
// headers) go heavy; h5/h6 (card titles, in-context labels) stay a lighter semibold so
|
||||
// dense screens don't turn into a wall of black text.
|
||||
h1: { fontWeight: 800, fontSize: 36, lineHeight: "40px", letterSpacing: "-0.02em" },
|
||||
h2: { fontWeight: 800, fontSize: 30, lineHeight: "34px", letterSpacing: "-0.02em" },
|
||||
h3: { fontWeight: 800, fontSize: 26, lineHeight: "30px", letterSpacing: "-0.01em" },
|
||||
h4: { fontWeight: 700, fontSize: 22, lineHeight: "26px", letterSpacing: "-0.01em" },
|
||||
h5: { fontWeight: 700, fontSize: 18, lineHeight: "22px" },
|
||||
h6: { fontWeight: 600, fontSize: 16, lineHeight: "20px" },
|
||||
subtitle1: { fontWeight: 600, fontSize: 14, lineHeight: "18px" },
|
||||
subtitle2: { fontWeight: 600, fontSize: 13, lineHeight: "17px" },
|
||||
body1: { fontWeight: 400, fontSize: 14, lineHeight: "18px" },
|
||||
body2: { fontWeight: 400, fontSize: 13, lineHeight: "17px" },
|
||||
caption: { fontWeight: 400, fontSize: 12, lineHeight: "16px", letterSpacing: 0 },
|
||||
overline: { fontWeight: 600, fontSize: 11, lineHeight: "14px", letterSpacing: "0.08em", textTransform: "uppercase" as const },
|
||||
overline: { fontWeight: 700, fontSize: 11, lineHeight: "14px", letterSpacing: "0.08em", textTransform: "uppercase" as const },
|
||||
caption1: { fontWeight: 500, fontSize: 12, lineHeight: "16px", letterSpacing: 0 },
|
||||
button: { textTransform: "capitalize" as const },
|
||||
button: { textTransform: "capitalize" as const, fontWeight: 600 },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -222,7 +241,9 @@ export const getTheme = (_mode: "light" | "dark") => {
|
||||
light: { palette: lightPalette, customShadows: buildCustomShadows(lightPalette) },
|
||||
dark: { palette: darkPalette, customShadows: buildCustomShadows(darkPalette) },
|
||||
},
|
||||
shape: { borderRadius: 10 },
|
||||
// Mockup radius reads noticeably rounder than the previous 10/8px defaults across cards,
|
||||
// stat tiles, kanban cards and buttons.
|
||||
shape: { borderRadius: 14 },
|
||||
typography: buildTypography() as any,
|
||||
} as any) as any;
|
||||
|
||||
@@ -249,8 +270,9 @@ export const getTheme = (_mode: "light" | "dark") => {
|
||||
defaultProps: { elevation: 0 },
|
||||
styleOverrides: {
|
||||
root: ({ theme }: any) => ({
|
||||
border: `1px solid ${theme.vars.palette.divider}`,
|
||||
borderRadius: 12,
|
||||
// No border -- mockup cards float on the grey page background purely via shadow.
|
||||
border: "none",
|
||||
borderRadius: 16,
|
||||
backgroundImage: "none",
|
||||
boxShadow: theme.vars.customShadows.section,
|
||||
}),
|
||||
@@ -260,7 +282,7 @@ export const getTheme = (_mode: "light" | "dark") => {
|
||||
defaultProps: { disableFocusRipple: true },
|
||||
styleOverrides: {
|
||||
root: ({ theme }: any) => ({
|
||||
borderRadius: 8,
|
||||
borderRadius: 10,
|
||||
boxShadow: theme.vars.customShadows.button,
|
||||
"&:hover": { boxShadow: theme.vars.customShadows.button },
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user