Files
LambdaMail/services/webmail/src/components/ui/Button.tsx
T
Lucas RayanandClaude Sonnet 5.5 e667843b59 feat(webmail): the new interface - logo, dark and light themes, command menu
A mark (a lambda on an ultramarine tile with the flap of an envelope), the
favicon, home-screen icons and an install manifest. Every colour is a variable
defined once per theme, and the Tailwind scales the screens already use read
them, so dark (the default) and light are a change of values and not of
classes. Geist is served by the app itself. Ctrl+K opens a command menu for
folders, actions, accounts and appearance, and typing something that matches no
command searches the mail; c writes a message and / goes to the search field.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 22:33:15 -03:00

59 lines
2.0 KiB
TypeScript

import React from 'react';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline' | 'danger' | 'ghost';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
}
/**
* Nocturne buttons are outlined, not filled.
*
* The primary action is a 1px accent border on transparent with a tinted hover
* and a deeper tint when pressed. A flooded accent was the loudest thing on
* every screen and made three buttons in a row compete with the content; an
* outline states the same intent at the weight it deserves. Focus is the
* system's own 2px accent ring from globals.css rather than a per-variant
* offset ring.
*/
export function Button({
variant = 'primary',
size = 'md',
className,
children,
...props
}: ButtonProps) {
const baseStyles =
'inline-flex items-center justify-center whitespace-nowrap font-medium rounded-[10px] border transition-colors duration-150 disabled:opacity-45 disabled:cursor-not-allowed';
const variants = {
primary:
'border-transparent bg-fill text-white shadow-sm hover:bg-fill-hover active:brightness-95',
secondary:
'border-ink/[0.14] text-slate-100 bg-transparent hover:bg-ink/[0.07] active:bg-ink/[0.14]',
outline:
'border-ink/[0.14] text-slate-300 bg-transparent hover:text-slate-100 hover:bg-ink/[0.07] active:bg-ink/[0.14]',
danger:
'border-rose-400 text-rose-400 bg-transparent hover:bg-rose-400/[0.12] active:bg-rose-400/[0.22]',
ghost:
'border-transparent text-indigo-500 bg-transparent hover:bg-indigo-500/10 active:bg-indigo-500/[0.18]',
};
const sizes = {
sm: 'px-2.5 py-1.5 text-xs gap-1.5',
md: 'px-3 py-2 text-sm gap-2',
lg: 'px-4 py-2.5 text-[15px] gap-2.5',
};
return (
<button
className={twMerge(clsx(baseStyles, variants[variant], sizes[size], className))}
{...props}
>
{children}
</button>
);
}