mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 05:12:33 +00:00
512 lines
12 KiB
CSS
512 lines
12 KiB
CSS
/* Import Google Fonts for trust and professionalism */
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
|
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600&display=swap');
|
|
|
|
/* Canonical theme tokens (colour + font-role CSS variables). Must precede the
|
|
Tailwind layers so the var-backed utility colours resolve. */
|
|
@import './styles/tokens.css';
|
|
|
|
/* Tailwind CSS imports */
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* Base layer - Typography and fundamental styles */
|
|
@layer base {
|
|
/* Set default font and improve text rendering */
|
|
html {
|
|
/* Body font role — themeable via styles/tokens.css (--font-body) */
|
|
font-family: var(--font-body);
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
text-rendering: optimizeLegibility;
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
body {
|
|
@apply font-sans;
|
|
line-height: 1.6;
|
|
|
|
/* Themes may supply a decorative `--app-gradient`; fall back to the flat
|
|
canvas colour when they don't. */
|
|
background: var(--app-gradient, var(--color-background));
|
|
color: var(--color-text-primary);
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
}
|
|
|
|
html[data-window='overlay'],
|
|
html[data-window='overlay'] body,
|
|
html[data-window='overlay'] #root,
|
|
html[data-window='mascot'],
|
|
html[data-window='mascot'] body,
|
|
html[data-window='mascot'] #root,
|
|
html[data-window='notch'],
|
|
html[data-window='notch'] body,
|
|
html[data-window='notch'] #root {
|
|
background: transparent;
|
|
overflow: hidden;
|
|
user-select: none;
|
|
}
|
|
|
|
@keyframes notch-pill-in {
|
|
from {
|
|
opacity: 0;
|
|
transform: scaleX(0.4) scaleY(0.7);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scaleX(1) scaleY(1);
|
|
}
|
|
}
|
|
|
|
@keyframes notch-bar {
|
|
0%,
|
|
100% {
|
|
transform: scaleY(0.5);
|
|
opacity: 0.6;
|
|
}
|
|
50% {
|
|
transform: scaleY(1.2);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes notch-dot {
|
|
0%,
|
|
80%,
|
|
100% {
|
|
transform: scale(0.6);
|
|
opacity: 0.4;
|
|
}
|
|
40% {
|
|
transform: scale(1);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes overlay-bubble-in {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px) scale(0.92);
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
}
|
|
}
|
|
|
|
* {
|
|
color: inherit;
|
|
}
|
|
|
|
/* Heading hierarchy for clear information architecture */
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
/* Heading font role + semantic text colour (themeable). */
|
|
@apply font-heading font-semibold;
|
|
@apply text-content;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
h1 {
|
|
@apply text-3xl lg:text-4xl;
|
|
}
|
|
h2 {
|
|
@apply text-2xl lg:text-3xl;
|
|
}
|
|
h3 {
|
|
@apply text-xl lg:text-2xl;
|
|
}
|
|
h4 {
|
|
@apply text-lg lg:text-xl;
|
|
}
|
|
|
|
/* Suppress the default browser focus ring (outline + tap highlight) but
|
|
leave borders and box-shadows alone — components rely on those for
|
|
selected / focus-visible styling (e.g. the runtime picker cards). */
|
|
* {
|
|
outline: none !important;
|
|
-webkit-tap-highlight-color: transparent !important;
|
|
}
|
|
|
|
*:focus {
|
|
outline: none !important;
|
|
}
|
|
|
|
button:focus,
|
|
a:focus,
|
|
input:focus,
|
|
textarea:focus,
|
|
select:focus,
|
|
[role='button']:focus,
|
|
[tabindex]:focus {
|
|
outline: none !important;
|
|
}
|
|
|
|
/* Smooth scrolling */
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
}
|
|
|
|
/* Component layer - Reusable patterns */
|
|
@layer components {
|
|
/* Button variants for consistent interaction design */
|
|
.btn-primary {
|
|
@apply bg-primary-500 hover:bg-primary-600 active:bg-primary-700;
|
|
@apply text-content-inverted font-medium;
|
|
@apply px-4 py-2 rounded-lg;
|
|
@apply transition-all duration-200 ease-in-out;
|
|
@apply shadow-soft hover:shadow-medium;
|
|
@apply focus:outline-none;
|
|
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
}
|
|
|
|
.btn-secondary {
|
|
@apply bg-surface hover:bg-surface-hover active:bg-surface-strong;
|
|
@apply text-content font-medium;
|
|
@apply px-4 py-2 rounded-lg border border-line-strong;
|
|
@apply transition-all duration-200 ease-in-out;
|
|
@apply focus:outline-none;
|
|
}
|
|
|
|
.btn-success {
|
|
@apply bg-sage-500 hover:bg-sage-600 active:bg-sage-700;
|
|
@apply text-content-inverted font-medium;
|
|
@apply px-4 py-2 rounded-lg;
|
|
@apply transition-all duration-200 ease-in-out;
|
|
@apply shadow-soft hover:shadow-medium;
|
|
@apply focus:outline-none;
|
|
}
|
|
|
|
.btn-danger {
|
|
@apply bg-coral-500 hover:bg-coral-600 active:bg-coral-700;
|
|
@apply text-content-inverted font-medium;
|
|
@apply px-4 py-2 rounded-lg;
|
|
@apply transition-all duration-200 ease-in-out;
|
|
@apply shadow-soft hover:shadow-medium;
|
|
@apply focus:outline-none;
|
|
}
|
|
|
|
/* Card components for content organization */
|
|
.card {
|
|
@apply bg-surface rounded-xl shadow-soft border border-line;
|
|
@apply p-6 text-content;
|
|
@apply transition-shadow duration-200;
|
|
}
|
|
|
|
.card-hover {
|
|
@apply card hover:shadow-medium;
|
|
}
|
|
|
|
.app-dotted-canvas {
|
|
background-color: transparent;
|
|
/* Dots follow the theme's content colour at low alpha (dark dots on light
|
|
themes, light dots on dark themes, accent-tinted on custom themes). */
|
|
background-image: radial-gradient(
|
|
circle at center,
|
|
rgb(var(--content) / 0.1) 1px,
|
|
transparent 1px
|
|
);
|
|
background-size: 18px 18px;
|
|
background-position: 0 0;
|
|
}
|
|
|
|
/* Input components with consistent styling */
|
|
.input-primary {
|
|
@apply w-full px-4 py-3 rounded-lg;
|
|
@apply border border-line focus:border-primary-500;
|
|
@apply bg-surface text-content placeholder-content-faint;
|
|
@apply transition-colors duration-200;
|
|
@apply focus:outline-none;
|
|
}
|
|
|
|
/* Status indicators */
|
|
.status-online {
|
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
|
@apply bg-sage-100 text-sage-700;
|
|
}
|
|
|
|
.status-offline {
|
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
|
@apply bg-surface-subtle text-content-secondary;
|
|
}
|
|
|
|
.status-warning {
|
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
|
@apply bg-amber-100 text-amber-700;
|
|
}
|
|
|
|
/* Navigation styles */
|
|
.nav-item {
|
|
@apply flex items-center px-4 py-2 text-sm font-medium rounded-lg;
|
|
@apply text-content-secondary hover:text-content hover:bg-surface-hover;
|
|
@apply transition-colors duration-150;
|
|
}
|
|
|
|
.nav-item-active {
|
|
@apply nav-item bg-surface-subtle text-content;
|
|
}
|
|
|
|
/* Message/chat specific styles */
|
|
.message-bubble {
|
|
@apply max-w-xs lg:max-w-md px-4 py-2 rounded-2xl;
|
|
@apply break-words;
|
|
}
|
|
|
|
.message-sent {
|
|
@apply message-bubble bg-primary-500 text-white ml-auto;
|
|
}
|
|
|
|
.message-received {
|
|
@apply message-bubble bg-surface-strong text-content;
|
|
}
|
|
|
|
/* Loading states */
|
|
.loading-pulse {
|
|
@apply animate-pulse bg-surface-subtle rounded;
|
|
}
|
|
|
|
/* Crypto price styling */
|
|
.price-positive {
|
|
@apply text-market-bullish font-mono font-medium;
|
|
}
|
|
|
|
.price-negative {
|
|
@apply text-market-bearish font-mono font-medium;
|
|
}
|
|
|
|
.price-neutral {
|
|
@apply text-market-neutral font-mono font-medium;
|
|
}
|
|
|
|
/* ============================================
|
|
Dark-mode overrides for @apply'd component classes.
|
|
Most classes are now token-driven (semantic surface/content/line) and flip
|
|
automatically, so only the tinted status badges — which keep a light accent
|
|
tint in light mode — still need an explicit dark override.
|
|
============================================ */
|
|
:root.dark .status-online {
|
|
background-color: rgba(52, 199, 89, 0.15);
|
|
color: theme('colors.sage.300');
|
|
}
|
|
|
|
:root.dark .status-warning {
|
|
background-color: rgba(232, 167, 40, 0.15);
|
|
color: theme('colors.amber.300');
|
|
}
|
|
}
|
|
|
|
/* Utility layer - Custom utilities */
|
|
@layer utilities {
|
|
/* Scrollbar styling for better UX */
|
|
.scrollbar-thin {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: rgb(163 163 163) transparent;
|
|
}
|
|
|
|
.scrollbar-thin::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.scrollbar-thin::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
background-color: rgb(163 163 163);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
|
background-color: rgb(115 115 115);
|
|
}
|
|
|
|
/* Hide scrollbar while keeping scroll behavior */
|
|
.scrollbar-hide {
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
}
|
|
.scrollbar-hide::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
/* Text selection styling */
|
|
.select-primary::selection {
|
|
@apply bg-primary-600 text-white;
|
|
}
|
|
|
|
/* Glass effect for light theme */
|
|
.glass {
|
|
backdrop-filter: blur(12px);
|
|
/* Themeable: surface tint + line border follow the active theme. */
|
|
background: rgb(var(--surface) / 0.8);
|
|
border: 1px solid rgb(var(--line) / 0.6);
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
:root.dark .glass {
|
|
/* Only the shadow needs a darker variant; colours come from the tokens. */
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
/* Animation utilities */
|
|
.animate-fade-in-up {
|
|
animation: fadeInUp 0.5s ease-out forwards;
|
|
}
|
|
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Modal animations */
|
|
.animate-fade-in {
|
|
animation: fadeIn 0.2s ease-out forwards;
|
|
}
|
|
|
|
.animate-slide-up {
|
|
animation: slideUp 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Safe area padding for mobile devices */
|
|
.safe-area-padding {
|
|
padding-top: env(safe-area-inset-top);
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
padding-left: env(safe-area-inset-left);
|
|
padding-right: env(safe-area-inset-right);
|
|
}
|
|
|
|
/* Skills table container */
|
|
.skills-table-container {
|
|
position: relative;
|
|
max-height: calc(3 * 2.5rem + 2.5rem + 1px); /* Header + 3 rows + border */
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.skills-table-scroll {
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
max-height: calc(3 * 2.5rem + 2.5rem + 1px);
|
|
}
|
|
|
|
.skills-table-header {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
backdrop-filter: blur(8px);
|
|
}
|
|
|
|
:root.dark .skills-table-header {
|
|
background: rgba(23, 23, 23, 0.9);
|
|
}
|
|
|
|
/* Gradient fade overlay at bottom */
|
|
.skills-table-container::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 50px;
|
|
background: linear-gradient(to bottom, transparent, rgba(245, 245, 245, 0.9));
|
|
pointer-events: none;
|
|
z-index: 10;
|
|
}
|
|
|
|
:root.dark .skills-table-container::after {
|
|
background: linear-gradient(to bottom, transparent, rgba(10, 10, 10, 0.9));
|
|
}
|
|
|
|
/* Hover overlay */
|
|
.skills-table-overlay {
|
|
z-index: 20;
|
|
}
|
|
|
|
.skills-table-container:hover .skills-table-overlay {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
}
|
|
|
|
/* Light mode (default) */
|
|
:root {
|
|
color-scheme: light;
|
|
}
|
|
|
|
/*
|
|
* Command palette + help overlay tokens — now thin aliases over the canonical
|
|
* theme tokens in styles/tokens.css, so the palette themes along with the rest
|
|
* of the app. Only the shadow (not a colour token) stays literal.
|
|
*/
|
|
:root {
|
|
--cmd-accent: rgb(var(--primary-500));
|
|
--cmd-surface: rgb(var(--surface));
|
|
--cmd-surface-elevated: rgb(var(--surface-subtle));
|
|
--cmd-foreground: rgb(var(--content));
|
|
--cmd-foreground-muted: rgb(var(--content-muted));
|
|
--cmd-border: rgb(var(--line));
|
|
--cmd-ring: var(--cmd-accent);
|
|
--cmd-overlay: rgb(var(--surface-overlay) / 0.5);
|
|
--cmd-shadow-palette: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
:root.dark {
|
|
--cmd-accent: rgb(var(--primary-400));
|
|
--cmd-surface: rgb(var(--surface));
|
|
--cmd-surface-elevated: rgb(var(--surface-muted));
|
|
--cmd-foreground: rgb(var(--content));
|
|
--cmd-foreground-muted: rgb(var(--content-muted));
|
|
--cmd-border: rgb(var(--line-strong));
|
|
--cmd-overlay: rgb(var(--surface-overlay) / 0.7);
|
|
--cmd-shadow-palette: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.25);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.cmd-palette-enter,
|
|
.cmd-palette-exit,
|
|
.cmd-help-enter,
|
|
.cmd-help-exit {
|
|
animation: none !important;
|
|
transition: none !important;
|
|
}
|
|
}
|