mirror of
https://github.com/mmahdium/portfolio.git
synced 2025-12-20 09:23:54 +01:00
320 lines
7.6 KiB
CSS
320 lines
7.6 KiB
CSS
@custom-variant dark (&:where(.dark &, :root.dark &, [data-theme="dark"] &));
|
|
|
|
@import "tailwindcss";
|
|
@import "@nuxt/ui";
|
|
|
|
@source "../../components/**/*.{vue,js,ts}";
|
|
@source "../../layouts/**/*.vue";
|
|
@source "../../pages/**/*.vue";
|
|
@source "../../app.vue";
|
|
@source "../../error.vue";
|
|
@source "../../composables/**/*.{js,ts}";
|
|
@source "../../plugins/**/*.{js,ts}";
|
|
@source "../../utils/**/*.{js,ts}";
|
|
@source "../../data/**/*.{js,ts}";
|
|
@source "../../../content/**/*.{md,json,yaml,yml}";
|
|
|
|
@theme {
|
|
--font-sans: 'Outfit', 'ui-sans-serif', 'system-ui', 'sans-serif';
|
|
--font-display: 'Fraunces', 'Roobert', 'Inter', 'serif';
|
|
}
|
|
|
|
@layer base {
|
|
@font-face {
|
|
font-family: "Roobert";
|
|
font-style: normal;
|
|
font-weight: 400;
|
|
src:
|
|
local(""),
|
|
url("/fonts/Roobert-Regular.woff2") format("woff2");
|
|
}
|
|
|
|
@font-face {
|
|
font-family: "Roobert";
|
|
font-style: normal;
|
|
font-weight: 500;
|
|
src:
|
|
local(""),
|
|
url("/fonts/Roobert-Medium.woff2") format("woff2");
|
|
}
|
|
|
|
@font-face {
|
|
font-family: "Roobert";
|
|
font-style: normal;
|
|
font-weight: 600;
|
|
src:
|
|
local(""),
|
|
url("/fonts/Roobert-SemiBold.woff2") format("woff2");
|
|
}
|
|
|
|
/* Persian variable font (Vazirmatn) — variable woff2 for best performance */
|
|
@font-face {
|
|
font-family: "Vazirmatn";
|
|
font-style: normal;
|
|
font-weight: 100 900;
|
|
font-display: swap;
|
|
src:
|
|
local(""),
|
|
url("/fonts/vazirmatn/webfonts/Vazirmatn[wght].woff2") format("woff2");
|
|
}
|
|
|
|
html {
|
|
overflow-y: scroll;
|
|
/* Avoid width variation */
|
|
}
|
|
|
|
html,
|
|
body,
|
|
#__nuxt,
|
|
#__layout {
|
|
@apply min-h-screen w-full;
|
|
background-color: #f2f5f9;
|
|
color-scheme: light;
|
|
}
|
|
|
|
.dark html,
|
|
.dark body,
|
|
.dark #__nuxt,
|
|
.dark #__layout {
|
|
/* fallback for SSR edge cases */
|
|
}
|
|
|
|
:root.dark #__nuxt,
|
|
:root.dark #__layout,
|
|
html.dark,
|
|
html.dark body,
|
|
html.dark #__nuxt,
|
|
html.dark #__layout {
|
|
background-color: #0b1220;
|
|
color-scheme: dark;
|
|
}
|
|
|
|
div,
|
|
span,
|
|
input,
|
|
textarea,
|
|
button,
|
|
select,
|
|
a {
|
|
@apply focus:outline-hidden;
|
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
|
}
|
|
|
|
/* Use the sans variable globally; switches automatically when we override variables */
|
|
html,
|
|
body {
|
|
font-family: var(--font-sans);
|
|
}
|
|
|
|
/* In Persian/RTL, prefer Vazirmatn for both body and headings via variables */
|
|
html[dir="rtl"],
|
|
html[lang^="fa"] {
|
|
--font-sans: 'Vazirmatn', 'Outfit', 'ui-sans-serif', 'system-ui', 'sans-serif';
|
|
--font-display: 'Vazirmatn', 'Roobert', 'Inter', 'serif';
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
|
|
h1,
|
|
h2,
|
|
h3 {
|
|
@apply font-display tracking-tight;
|
|
}
|
|
|
|
h1 {
|
|
@apply text-3xl sm:text-4xl;
|
|
}
|
|
|
|
h2 {
|
|
@apply text-2xl sm:text-3xl;
|
|
}
|
|
|
|
h3 {
|
|
@apply text-xl sm:text-2xl;
|
|
}
|
|
}
|
|
|
|
@layer utilities {
|
|
.primary-text {
|
|
@apply text-primary-500 dark:text-primary-400;
|
|
}
|
|
|
|
.primary-text-muted {
|
|
@apply text-slate-500 dark:text-slate-400;
|
|
}
|
|
|
|
.decorated {
|
|
@apply underline underline-offset-8 decoration-primary-500 dark:decoration-primary-400;
|
|
}
|
|
|
|
/* Soft text color helpers */
|
|
.muted {
|
|
@apply text-slate-600 dark:text-slate-300;
|
|
}
|
|
|
|
.chip-base {
|
|
@apply inline-flex items-center gap-1.5 rounded-xl px-2.5 py-1 text-xs font-medium ring-1 ring-slate-200/70 bg-white/70 text-slate-700 shadow-sm backdrop-blur-sm dark:bg-white/5 dark:text-slate-300 dark:ring-slate-700/50;
|
|
}
|
|
|
|
/* Hide scrollbars for overflow containers */
|
|
.no-scrollbar {
|
|
-ms-overflow-style: none;
|
|
/* IE and Edge */
|
|
scrollbar-width: none;
|
|
/* Firefox */
|
|
}
|
|
|
|
.no-scrollbar::-webkit-scrollbar {
|
|
display: none;
|
|
/* Chrome, Safari, Opera */
|
|
}
|
|
|
|
/* Safe area bottom padding for devices with home indicator */
|
|
.safe-bottom {
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
|
|
/* Global hover utilities for consistent interactions */
|
|
.hover-ring-tint {
|
|
/* Ring-only hover with subtle background tint; no scale; focus-visible support */
|
|
@apply transition-colors duration-200 ease-out ring-0 hover:ring-2 hover:ring-primary-500 dark:hover:ring-primary-400 hover:bg-primary-50/60 dark:hover:bg-primary-400/10 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900;
|
|
}
|
|
|
|
.link-hover-clean {
|
|
/* Clean link hover with color shift and underline */
|
|
@apply transition-colors duration-200 hover:text-primary-600 dark:hover:text-primary-400 hover:underline underline-offset-4 decoration-primary-500/60 dark:decoration-primary-400/60;
|
|
}
|
|
}
|
|
|
|
@layer utilities {
|
|
|
|
/* Minimal hover: subtle bg tint, slight shadow, no ring/border change */
|
|
.hover-minimal {
|
|
@apply transition-all duration-150 ease-out hover:bg-gray-100/70 dark:hover:bg-white/5 hover:shadow-sm;
|
|
}
|
|
}
|
|
|
|
@layer utilities {
|
|
|
|
/* Minimal outlined button: persistent subtle border and light background; hover handled separately */
|
|
.btn-outline-minimal {
|
|
@apply border border-gray-300/60 dark:border-gray-600/50 bg-gray-100/30 dark:bg-white/5;
|
|
}
|
|
}
|
|
|
|
@layer utilities {
|
|
|
|
/* Chip-style icon button inspired by SkillGrid: subtle ring, soft bg, minimal hover */
|
|
.chip-button {
|
|
@apply inline-flex items-center justify-center h-10 w-10 rounded-xl ring-1 ring-slate-200/70 dark:ring-slate-700/50 bg-white/70 dark:bg-white/5 text-slate-700 dark:text-slate-200 shadow-sm backdrop-blur-sm transition-colors duration-150 ease-out hover:bg-white/80 dark:hover:bg-white/10;
|
|
}
|
|
}
|
|
|
|
/* View Transitions ripple for theme switch (best-practice) */
|
|
@supports (view-transition-name: theme) {
|
|
:root {
|
|
view-transition-name: theme;
|
|
}
|
|
|
|
/* Configure transition pseudo-elements */
|
|
::view-transition-old(theme),
|
|
::view-transition-new(theme) {
|
|
animation-duration: var(--vtx-duration, 500ms);
|
|
animation-timing-function: var(--vtx-easing, ease-in-out);
|
|
}
|
|
|
|
/* Old view: let new view reveal with ripple */
|
|
::view-transition-old(theme) {
|
|
animation: none;
|
|
}
|
|
|
|
/* New view: reveal from click origin via expanding circle */
|
|
::view-transition-new(theme) {
|
|
animation-name: vtx-reveal;
|
|
}
|
|
|
|
@keyframes vtx-reveal {
|
|
from {
|
|
clip-path: circle(0 at var(--vtx-x, 50vw) var(--vtx-y, 50vh));
|
|
}
|
|
|
|
to {
|
|
clip-path: circle(var(--vtx-end, 200vmax) at var(--vtx-x, 50vw) var(--vtx-y, 50vh));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Fallback fade when View Transitions unsupported or prefers-reduced-motion */
|
|
.vtx-fade #__nuxt {
|
|
animation: vtx-fade var(--vtx-duration, 500ms) var(--vtx-easing, ease-in-out);
|
|
}
|
|
|
|
@keyframes vtx-fade {
|
|
from {
|
|
opacity: 0.92;
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Reduced motion guard */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
:root {
|
|
view-transition-name: none;
|
|
}
|
|
}
|
|
|
|
/* Locale switch UX: top progress bar and quick page fade */
|
|
@layer utilities {
|
|
.locale-progress {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
height: 3px;
|
|
width: 100%;
|
|
z-index: 1000;
|
|
pointer-events: none;
|
|
/* Use primary color gradient; adapt in dark mode */
|
|
background-image: linear-gradient(to right,
|
|
rgb(99 102 241 / 0.9),
|
|
/* indigo-500 */
|
|
rgb(139 92 246 / 0.9)
|
|
/* violet-500 */
|
|
);
|
|
transform-origin: left center;
|
|
animation: locale-progress-grow 600ms ease-out forwards;
|
|
}
|
|
|
|
@keyframes locale-progress-grow {
|
|
from {
|
|
transform: scaleX(0);
|
|
opacity: 0.85;
|
|
}
|
|
|
|
to {
|
|
transform: scaleX(1);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Quick page fade during locale switching (client-only via documentElement class) */
|
|
.locale-switching #__nuxt {
|
|
animation: locale-fade 600ms ease-out both;
|
|
}
|
|
|
|
@keyframes locale-fade {
|
|
from {
|
|
opacity: 0.92;
|
|
filter: saturate(0.98);
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
filter: saturate(1);
|
|
}
|
|
}
|