refactor(ui): editorial monochrome design refresh (#473)
* refactor(ui): editorial monochrome design refresh Design-system layer only, no functional changes. Cascades app-wide from 8 files in the primitives + tokens layer. - Swap Fraunces → Hedvig Letters Serif for display typography (single-weight; drop font-medium from CardTitle and PageHeader) - Token sweep: pure white background, warm beige secondary (40 11% 89%), achromatic primary, calibrated 45 5% 85% border, halved-opacity shadows - Flatten Card: remove shadow, rounded-xl → rounded-lg, full-opacity border (was border-border/60) - Flatten Button: remove shadow-sm, remove active:scale-[0.98], drop outline border morph, transition-all 300ms → transition-colors 150ms - Soften Dialog overlay (bg-black/80 → bg-black/40 with dark variant) and use halved --shadow-md token on DialogContent - Sidebar: bg-card/90 → bg-background, full-opacity hairline border, warm-beige active state (bg-secondary), hover bg-secondary/60 - .hover-lift utility: replace translateY + box-shadow with flat background-color shift - PageHeader title: text-2xl font-medium → text-3xl md:text-4xl (no font-medium) - CLAUDE.md Brand & Aesthetic, Typography, and Forbidden Patterns sections rewritten to reflect new system Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ui): address PR review - Remove dead .hover-lift utility (no callers; CLAUDE.md now bans hover-lift patterns, so leaving the class would contradict the docs) - Bump Dialog overlay bg-black/40 → bg-black/50 — gives a more perceptible separation layer over the new pure-white background while keeping the lighter editorial feel Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
2ed8096150
commit
ba45b8f661
@@ -354,13 +354,13 @@ Swedish sole traders (enskild firma) and small business owners (aktiebolag) who
|
||||
|
||||
### Brand & Aesthetic
|
||||
|
||||
**Minimal. Sharp. Efficient.** The interface should feel like a well-made instrument: considered, quiet, and confident. Reference: Mercury (banking). Anti-reference: enterprise software (SAP/Oracle density).
|
||||
**Editorial monochrome.** Paper-white surfaces, hairline borders, serif headlines. The interface should feel like a well-made instrument — considered, quiet, confident. Anti-references: enterprise software (SAP/Oracle density), neon SaaS coldness.
|
||||
|
||||
- **Palette**: Grayscale foundation with restrained semantic colors — sage green (success/balance), terracotta (errors/overdue), ochre (warnings/attention). No loud brand color.
|
||||
- **Typography**: Fraunces (serif) for display headings, Geist (sans) for body. Tabular numbers everywhere financial data appears.
|
||||
- **Surfaces**: White/near-white cards on light gray backgrounds. Subtle borders (60% opacity). Soft shadows. Dark mode follows the same restraint.
|
||||
- **Palette**: Achromatic foundation. Pure white background, warm beige (`40 11% 89%`) for chips / active sidebar / hover / secondary buttons. Achromatic primary (no cool tint). Semantic colors (`--success` sage, `--warning` ochre, `--destructive` terracotta) exist but are **data-only** — they appear in charts and financial numbers (positive/negative deltas), never as chrome backgrounds. In chrome, only `--destructive` survives.
|
||||
- **Typography**: Hedvig Letters Serif for display headings, Geist (sans) for body, forms, and tables. Hedvig is single-weight (400) — do not apply `font-medium` to display text; its natural high-contrast strokes carry the weight. Tabular numbers everywhere financial data appears.
|
||||
- **Surfaces**: Cards sit flat on the page — no shadow, full-opacity hairline border (`border-border`), `rounded-lg` (8px). Card background matches page background; the border carries hierarchy. Dark mode drops the warm tint from secondary for a pure-gray mood shift; light mode keeps the beige.
|
||||
- **Spacing**: Generous whitespace. Dense data (tables, ledgers) uses tighter spacing but never feels cramped.
|
||||
- **Motion**: Subtle and purposeful. Stagger animations for list entry, spring easing for feedback. Never decorative.
|
||||
- **Motion**: Functional, not decorative. No press-scale, no hover-lift, no spring overshoot. Hover state is a flat background shift (`bg-secondary/60`). `transition-colors duration-150` is the default. Stagger animations on list entry are fine. Respect `prefers-reduced-motion` (already wired).
|
||||
- **Icons**: Lucide — 15px in navigation, slightly larger in empty states.
|
||||
|
||||
### Design Principles
|
||||
@@ -426,11 +426,11 @@ Never render raw `{x.invoice_date}` directly — always route through `formatDat
|
||||
**Currency.** `formatCurrency(n, currency?)` from `lib/utils.ts`. Default SEK.
|
||||
|
||||
**Typography.**
|
||||
- Page title: `<h1 className="font-display text-2xl md:text-3xl font-medium tracking-tight">` (or use `PageHeader`).
|
||||
- Card title: `<CardTitle className="text-base">` for sections, default for primary cards.
|
||||
- Page title: use `PageHeader` (renders `font-display text-3xl md:text-4xl tracking-tight`). Do not hand-roll an `<h1>`.
|
||||
- Card title: `<CardTitle className="text-base">` for sections, default for primary cards. The primitive already drops `font-medium` — do not add it back.
|
||||
- Section divider header inside a page: `<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">`.
|
||||
- Headline number: `font-display text-xl font-medium tabular-nums`.
|
||||
- Display font (`font-display`, Fraunces) reserved for h1/h2/h3 and primary financial numbers.
|
||||
- Headline number: `font-display text-xl tabular-nums`. No `font-medium` — Hedvig's natural weight carries the gravitas.
|
||||
- Display font (`font-display`, Hedvig Letters Serif) reserved for h1/h2/h3 and primary financial numbers. If a specific `font-display` numeral reads weak inside a compact metric card, override that call site with `font-sans tabular-nums` (Geist) — better legibility on small numerals.
|
||||
|
||||
**Forbidden / dead patterns.**
|
||||
- Page descriptions that paraphrase the page title (e.g. `<PageHeader title="Fakturor" description="Hantera dina fakturor">`) → drop the description.
|
||||
@@ -438,3 +438,9 @@ Never render raw `{x.invoice_date}` directly — always route through `formatDat
|
||||
- Mobile-specific `<select>` duplicating desktop tabs in code — use a single Tabs primitive or a single grouped `Select`.
|
||||
- Hand-rolled icon buttons smaller than `h-10 w-10`. Use shadcn `Button size="icon"`.
|
||||
- Color-coded status using full-rainbow Tailwind palette (`bg-amber-100`, `bg-emerald-500/10`, etc.). Use Badge variants tied to the brand palette.
|
||||
- `shadow-sm` / `shadow-md` / `shadow-lg` on cards, buttons, or list items. The aesthetic is flat-with-hairlines — surfaces use `border-border`, not elevation. Shadows survive only on dialogs/popovers/dropdowns (anything that overlays the page).
|
||||
- `active:scale-[...]` on buttons. Buttons do not bounce.
|
||||
- `bg-gradient-to-*` on page or card backgrounds. Flat surfaces only.
|
||||
- `font-medium` on display elements (`font-display`, h1/h2/h3, CardTitle, PageHeader title). Hedvig is single-weight by design.
|
||||
- `rounded-xl` (12px) on cards. Cards are `rounded-lg` (8px). `rounded-xl` survives only on prominent hero-style surfaces if absolutely needed.
|
||||
- Opacity-suffixed border classes (`border-border/30`, `border-border/60`) on cards and primary surfaces. Use full-opacity `border-border` — the new border token is calibrated for that.
|
||||
|
||||
+22
-37
@@ -11,35 +11,35 @@
|
||||
|
||||
:root {
|
||||
color-scheme: light;
|
||||
/* Grayscale Chrome Palette */
|
||||
/* Grayscale Chrome Palette — paper white + warm beige */
|
||||
|
||||
--background: 0 0% 97%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 9%;
|
||||
|
||||
--card: 0 0% 99%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 9%;
|
||||
|
||||
--popover: 0 0% 99%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 9%;
|
||||
|
||||
--primary: 210 5% 25%;
|
||||
--primary: 0 0% 9%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
|
||||
--secondary: 0 0% 94%;
|
||||
--secondary: 40 11% 89%; /* Warm beige paper chip */
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
|
||||
--muted: 0 0% 92%;
|
||||
--muted: 40 8% 93%;
|
||||
--muted-foreground: 0 0% 40%;
|
||||
|
||||
--accent: 0 0% 92%;
|
||||
--accent: 40 8% 93%;
|
||||
--accent-foreground: 0 0% 9%;
|
||||
|
||||
--destructive: 6 36% 48%; /* Muted terracotta */
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
|
||||
--border: 0 0% 90%;
|
||||
--input: 0 0% 90%;
|
||||
--ring: 210 5% 25%;
|
||||
--border: 45 5% 85%;
|
||||
--input: 45 5% 85%;
|
||||
--ring: 0 0% 9%;
|
||||
|
||||
/* Radius */
|
||||
--radius: 0.5rem;
|
||||
@@ -61,13 +61,13 @@
|
||||
--chart-4: 0 0% 50%;
|
||||
|
||||
/* Custom tokens */
|
||||
--font-display: var(--font-fraunces), Georgia, serif;
|
||||
--font-display: var(--font-hedvig-serif), Georgia, serif;
|
||||
--font-body: var(--font-geist-sans), system-ui, sans-serif;
|
||||
|
||||
/* Shadows — cool-toned */
|
||||
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.06);
|
||||
--shadow-lg: 0 12px 40px rgba(15, 23, 42, 0.08);
|
||||
/* Shadows — barely-there; chrome relies on hairline borders */
|
||||
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.03), 0 1px 1px rgba(15, 23, 42, 0.02);
|
||||
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.03);
|
||||
--shadow-lg: 0 12px 40px rgba(15, 23, 42, 0.05);
|
||||
|
||||
/* Animation */
|
||||
--ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
@@ -83,13 +83,13 @@
|
||||
--background: 0 0% 7%;
|
||||
--foreground: 0 0% 87%;
|
||||
|
||||
--card: 0 0% 10%;
|
||||
--card: 0 0% 11%;
|
||||
--card-foreground: 0 0% 87%;
|
||||
|
||||
--popover: 0 0% 10%;
|
||||
--popover: 0 0% 11%;
|
||||
--popover-foreground: 0 0% 87%;
|
||||
|
||||
--primary: 210 4% 82%;
|
||||
--primary: 0 0% 87%;
|
||||
--primary-foreground: 0 0% 7%;
|
||||
|
||||
--secondary: 0 0% 14%;
|
||||
@@ -104,9 +104,9 @@
|
||||
--destructive: 6 32% 56%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
|
||||
--border: 0 0% 18%;
|
||||
--input: 0 0% 18%;
|
||||
--ring: 210 4% 82%;
|
||||
--border: 45 4% 18%;
|
||||
--input: 45 4% 18%;
|
||||
--ring: 0 0% 87%;
|
||||
|
||||
--success: 155 22% 48%;
|
||||
--success-foreground: 0 0% 100%;
|
||||
@@ -278,17 +278,6 @@ h1, h2, h3 {
|
||||
animation: scaleIn var(--duration-base) var(--ease-out);
|
||||
}
|
||||
|
||||
/* Refined hover effects */
|
||||
.hover-lift {
|
||||
transition: transform var(--duration-fast) var(--ease-out),
|
||||
box-shadow var(--duration-fast) var(--ease-out);
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Soft focus states */
|
||||
.focus-ring:focus-visible {
|
||||
outline: none;
|
||||
@@ -357,8 +346,4 @@ input[type="number"] {
|
||||
animation: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
import type { Metadata, Viewport } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import { Fraunces } from "next/font/google";
|
||||
import { Hedvig_Letters_Serif } from "next/font/google";
|
||||
import Script from "next/script";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
@@ -23,10 +23,11 @@ const geistMono = Geist_Mono({
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const fraunces = Fraunces({
|
||||
variable: "--font-fraunces",
|
||||
const hedvigSerif = Hedvig_Letters_Serif({
|
||||
variable: "--font-hedvig-serif",
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
weight: "400",
|
||||
});
|
||||
|
||||
export function generateMetadata(): Metadata {
|
||||
@@ -59,7 +60,7 @@ export default function RootLayout({
|
||||
}>) {
|
||||
const branding = getBranding();
|
||||
return (
|
||||
<html lang="sv" translate="no" suppressHydrationWarning className={`${geistSans.variable} ${geistMono.variable} ${fraunces.variable}`}>
|
||||
<html lang="sv" translate="no" suppressHydrationWarning className={`${geistSans.variable} ${geistMono.variable} ${hedvigSerif.variable}`}>
|
||||
<head>
|
||||
<meta name="google" content="notranslate" />
|
||||
<link rel="apple-touch-icon" href={branding.appleTouchIconPath} />
|
||||
|
||||
@@ -188,7 +188,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
<>
|
||||
{/* Desktop sidebar */}
|
||||
<aside className="hidden md:fixed md:inset-y-0 md:flex md:w-64 md:flex-col">
|
||||
<div className="flex min-h-0 flex-1 flex-col border-r border-border/30 bg-card/90">
|
||||
<div className="flex min-h-0 flex-1 flex-col border-r border-border bg-background">
|
||||
<div className="flex flex-1 flex-col overflow-y-auto pt-7 pb-4">
|
||||
{/* Company switcher */}
|
||||
<div className="px-5 mb-8">
|
||||
@@ -235,8 +235,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
? cn(
|
||||
'transition-colors duration-150',
|
||||
active
|
||||
? 'bg-primary/12 text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
|
||||
? 'bg-secondary text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
|
||||
)
|
||||
: 'text-muted-foreground/40 cursor-not-allowed'
|
||||
)
|
||||
@@ -306,8 +306,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
? cn(
|
||||
'transition-colors duration-150',
|
||||
active
|
||||
? 'bg-primary/12 text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
|
||||
? 'bg-secondary text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
|
||||
)
|
||||
: 'text-muted-foreground/40 cursor-not-allowed'
|
||||
)
|
||||
@@ -364,8 +364,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
? cn(
|
||||
'transition-colors duration-150',
|
||||
active
|
||||
? 'bg-primary/12 text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
|
||||
? 'bg-secondary text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
|
||||
)
|
||||
: 'text-muted-foreground/40 cursor-not-allowed'
|
||||
)
|
||||
@@ -403,8 +403,8 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
? cn(
|
||||
'transition-colors duration-150',
|
||||
active
|
||||
? 'bg-primary/12 text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/40'
|
||||
? 'bg-secondary text-foreground font-medium'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-secondary/60'
|
||||
)
|
||||
: 'text-muted-foreground/40 cursor-not-allowed'
|
||||
)
|
||||
@@ -430,7 +430,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
</div>
|
||||
|
||||
{/* Support + Logout */}
|
||||
<div className="flex-shrink-0 px-3 py-3 border-t border-border/30 space-y-1">
|
||||
<div className="flex-shrink-0 px-3 py-3 border-t border-border space-y-1">
|
||||
<div className="px-3 py-1.5">
|
||||
<SupportLink variant="muted" />
|
||||
</div>
|
||||
@@ -762,7 +762,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, un
|
||||
</div>
|
||||
|
||||
{/* Support + Logout */}
|
||||
<div className="px-2 py-2 mt-1 border-t border-border/30 space-y-1">
|
||||
<div className="px-2 py-2 mt-1 border-t border-border space-y-1">
|
||||
<div className="px-3 py-2">
|
||||
<SupportLink variant="muted" />
|
||||
</div>
|
||||
|
||||
@@ -4,16 +4,16 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",
|
||||
"inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-sm hover:bg-primary/80 active:scale-[0.98]",
|
||||
"bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/80 active:scale-[0.98]",
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-transparent hover:bg-secondary hover:border-secondary-foreground/20",
|
||||
"border border-input bg-transparent hover:bg-secondary",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/70",
|
||||
ghost:
|
||||
@@ -21,7 +21,7 @@ const buttonVariants = cva(
|
||||
link:
|
||||
"text-primary underline-offset-4 hover:underline",
|
||||
success:
|
||||
"bg-success text-success-foreground shadow-sm hover:bg-success/80 active:scale-[0.98]",
|
||||
"bg-success text-success-foreground hover:bg-success/90",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-5 py-2 rounded-lg",
|
||||
|
||||
@@ -8,7 +8,7 @@ const Card = React.forwardRef<
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-xl border border-border/60 bg-card text-card-foreground shadow-[var(--shadow-sm)]",
|
||||
"rounded-lg border border-border bg-card text-card-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -35,7 +35,7 @@ const CardTitle = React.forwardRef<
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"font-display text-xl font-medium leading-none tracking-tight",
|
||||
"font-display text-xl leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -20,7 +20,7 @@ const DialogOverlay = React.forwardRef<
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"fixed inset-0 z-50 bg-black/50 dark:bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -37,7 +37,7 @@ const DialogContent = React.forwardRef<
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 max-h-[calc(100dvh-2rem)] overflow-y-auto data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-[var(--shadow-md)] duration-200 max-h-[calc(100dvh-2rem)] overflow-y-auto data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -10,7 +10,7 @@ export function PageHeader({ title, description, action }: PageHeaderProps) {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between mb-8">
|
||||
<div>
|
||||
<h1 className="font-display text-2xl font-medium tracking-tight">{title}</h1>
|
||||
<h1 className="font-display text-3xl md:text-4xl tracking-tight">{title}</h1>
|
||||
{description && (
|
||||
<p className="text-muted-foreground mt-1 text-balance">{description}</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user