Files
2026-hypn0-site/hypn0/templates/css/tailwind-custom.css
T
2026-09-26 16:29:15 +03:00

678 lines
22 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
tailwind-custom.css — кастомные Tailwind-директивы проекта hypn0
(c) 2026, Sergei Erjemin
{% comment %}
ВНИМАНИЕ: этот файл не просто CSS! Это Django-шаблон, который подключается через
include-тег в инлайновый блок <style type="text/tailwindcss"> шаблона _base.html.
ВАЖНО: этот файл содержит ТОЛЬКО директивы @theme/@layer, которые обязаны
пройти через компилятор Tailwind, поэтому подключать его напрямую через
<link rel="stylesheet"> НЕЛЬЗЯ — обычный браузерный CSS-парсер не понимает
@theme/@apply и просто отбросит эти правила (проверено на практике: именно
так "молчал" прежний hypn0.css).
ОЧЕНЬ ВАЖНО: Так как этот CSS-файл сам является Django-шаблоном (подключается
инлайн через include-тег движка шаблонов), то внутри него НЕЛЬЗЯ писать буквенные
примеры Django-тегов вида "открыть-процент include ... процент-закрыть" —
движок шаблонов исполнит их по-настоящему, даже внутри CSS-комментария.
Это может привести к бесконечным рекурсиям (файл случайно подключал
сам себя) и другим непредсказуемым последствиям.
Если использовать комментарии для шаблонов Django, как здесь, то это безопасно.
Файл используется в ДВУХ местах одновременно:
1. В dev — подключается инклюд-тегом движка шаблонов Django ВНУТРИ
инлайнового блока <style type="text/tailwindcss"> в _base.html.
Именно этот блок сканирует и компилирует CDN-скрипт
@tailwindcss/browser прямо в браузере (он видит только финальный DOM,
а не внешние .css-файлы, подключённые через <link>).
2. В prod — подключается через нативный CSS @import из
frontend-assembly/tailwind/input.css в сборщике scripts/build-tailwind.sh.
Результат Tailwind CLI собирает в public/static/css/tailwind.min.css
(см. frontend-assembly/tailwind/package.json).
Поэтому здесь НЕТ строки `@import "tailwindcss";` — её добавляет каждый
"потребитель" данного файла САМ, непосредственно перед подключением этого
файла: инлайновый блок в _base.html (в dev) и input.css (в prod). Без
этого импорта директива @apply внутри @layer base ниже не сможет найти
используемые в ней стандартные классы (border-slate-500 и т.п.) и упадёт
с ошибкой "Cannot apply unknown utility class" — при этом в dev это ломает
ВООБЩЕ ВСЮ генерацию Tailwind CSS на странице (проверено на практике).
{% endcomment %} */
@theme { /* -- Кастомные переменные цветов -- {% comment %}
@theme задаёт кастомные токены цвета, которые можно использовать
в Tailwind-классах через синтаксис, например, text-(--color-link)
Это позволяет менять стили глобально, не бегая по шаблонам и не меняя конкретные
классы, а просто перекрашивая токены цвета в одном месте. {% endcomment %} */
--color-link: var(--color-slate-500);
--color-link-hover: var(--color-indigo-800);
}
@custom-variant dark (&:where(.dark, .dark *));
@layer base { /* -- СЛОЙ BASE для Tailwind v4.0+ — см. https://tailwindcss.com/docs/layer-directive --{% comment %}
Типовой стиль ссылки "по умолчанию" для всего проекта.
Поэтому обычную текстовую ссылку теперь можно писать в шаблоне вообще без
class="..." (просто тег a с href и текстом внутри, без единого класса) —
и она сразу получит фирменный вид сайта (пунктирное подчёркивание,
типовой цвет и ховер). Именно так сделано в block/breadcrumbs.html.
ВАЖНЫЙ НЮАНС для ссылок, которые НЕ должны выглядеть как "типовая
текстовая ссылка" (например, пункты меню-кнопок в block/header.html —
там ссылка визуально оформлена как кнопка, а не как подчёркнутый текст):
Tailwind v4 использует настоящие CSS cascade layers (@layer base,
components, utilities...), и по их правилам слой `utilities` ВСЕГДА
побеждает слой `base` независимо от специфичности селектора — НО только
для тех CSS-свойств, которые реально заданы в utilities-классе на этом
элементе. Если у пункта меню нет своего класса для color/border (как
сейчас в header.html — там заданы только фон и отступы через
hover:bg-gray-200, px-4 и т.п.), то свойства color/border-bottom он
"унаследует" именно отсюда, а не из своего блочного оформления —
родительский text-slate-900 на <menu> тут не поможет, т.к. у самого
<a> появится СВОЙ явный color.
Поэтому такие ссылки-кнопки должны явно "отключать" типовой стиль:
добавить border-0 (убирает пунктир) и свой text-slate-900 dark:text-
slate-100 (задаёт свой цвет вместо цвета типовой ссылки) — это и сделано
в block/header.html.{% endcomment %} */
[x-cloak] {
display: none !important;
}
a {
@apply border-b border-dotted
/* В светлой теме: глубокий изумрудный, в темной: блеклый лайм (отлично читается) */
border-emerald-600 dark:border-lime-500
text-emerald-600 dark:text-lime-400 transition duration-800
/* При наведении: взрываем кислоту, но контролируемо */
hover:text-emerald-500 dark:hover:text-lime-300 delay-300 duration-100
hover:border-solid transition-colors transition duration-100;
}
}
/* ==========================================================================
КОМПОНЕНТЫ КАРТОЧКИ ГАЛЕРЕИ (HYPNO GALLERY CARD)
========================================================================== */
@layer components {
/* Карточка картины */
.card {
@apply aspect-square rounded-2xl border border-stone-300/80 dark:border-zinc-800/80
overflow-hidden relative transition-all duration-300
hover:scale-[1.03] hover:shadow-2xl hover:border-amber-500/60 dark:hover:border-amber-500/50
flex items-center justify-center select-none shadow-sm
bg-[var(--card-bg-light)] dark:bg-[var(--card-bg-dark)];
}
/* Ссылка и оверлей */
.card-link {
@apply absolute inset-0 z-10 flex items-center justify-center p-3 sm:p-4
border-0 border-none no-underline hover:border-0 hover:border-none focus:outline-none;
}
/* Контейнер SVG */
.card-svg {
@apply w-full h-full flex items-center justify-center drop-shadow-sm
transition-transform duration-300 group-hover:scale-105 border-0;
}
/* Верхний бейдж хэша и уровня */
.card-tag {
@apply text-[10px] font-mono font-bold tracking-tight px-1.5 py-0.5 rounded
backdrop-blur-md transition-opacity;
}
.card-tag-immortal {
@apply bg-emerald-950/80 text-emerald-300 border border-emerald-500/30 opacity-80 group-hover:opacity-100;
}
.card-tag-curated {
@apply bg-cyan-950/80 text-cyan-300 border border-cyan-500/30 opacity-80 group-hover:opacity-100;
}
.card-tag-fresh {
@apply bg-stone-900/70 text-stone-200 border border-white/10 opacity-75 group-hover:opacity-100;
}
/* Нижний бейдж статистики */
.card-stat {
@apply inline-flex items-center gap-1 font-mono px-2 py-0.5 rounded-full
backdrop-blur-md shadow-sm transition-opacity;
}
.card-stat-immortal {
@apply text-[11px] font-bold bg-stone-900/80 text-emerald-400 border border-emerald-500/20 opacity-90 group-hover:opacity-100;
}
.card-stat-curated {
@apply text-[11px] font-bold bg-stone-900/80 text-cyan-400 border border-cyan-500/20 opacity-90 group-hover:opacity-100;
}
.card-stat-fresh {
@apply text-[11px] font-bold bg-stone-900/80 text-amber-400 border border-amber-500/20 opacity-90 group-hover:opacity-100;
}
.card-stat-views {
@apply text-[10px] bg-stone-900/80 text-stone-400 px-1.5 py-0.5 opacity-0 group-hover:opacity-100;
}
/* Панели карточки */
.card-top {
@apply absolute top-2 left-2 z-20 pointer-events-none flex items-center gap-1;
}
.card-bottom {
@apply absolute bottom-2 left-2 right-2 z-20 flex items-center justify-between pointer-events-none;
}
/* Заглушка noscript внутри карточки */
.card-noscript {
@apply flex flex-col items-center justify-center p-3 text-center gap-1;
}
.card-noscript-warn {
@apply text-[11px] font-bold text-amber-600 dark:text-amber-400;
}
.card-noscript-title {
@apply text-[10px] text-stone-500 dark:text-stone-400 underline;
}
}
/* ==========================================================================
КОМПОНЕНТЫ СТРАНИЦЫ АРХИВА ГАЛЕРЕИ (GALLERY ARCHIVE)
========================================================================== */
@layer components {
/* Навигация и заголовок архива */
.archive-back {
@apply inline-flex items-center gap-2 text-xs font-bold uppercase tracking-wider
text-stone-500 hover:text-stone-900 dark:text-stone-400 dark:hover:text-stone-100
transition-colors border-0 no-underline;
}
.archive-phase {
@apply text-xs font-mono text-stone-500 dark:text-stone-400;
}
.archive-header {
@apply border-b border-stone-200 dark:border-zinc-800 pb-6 space-y-2;
}
.archive-title {
@apply text-3xl font-black uppercase tracking-tight text-stone-900 dark:text-stone-100;
}
.archive-badge {
@apply text-xs font-bold font-mono px-2.5 py-1 rounded-full
bg-purple-100 dark:bg-purple-950/70 text-purple-700 dark:text-purple-400
border border-purple-300 dark:border-purple-800/50;
}
.archive-desc {
@apply text-sm text-stone-500 dark:text-stone-400;
}
/* Панель фильтров архива */
.archive-filter-bar {
@apply flex flex-col md:flex-row md:items-center justify-between gap-4
bg-stone-100/70 dark:bg-zinc-900/70 p-3 rounded-2xl
border border-stone-200 dark:border-zinc-800 backdrop-blur-sm;
}
.archive-tab {
@apply inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-bold
transition-all border-0 no-underline;
}
.archive-tab-all-active {
@apply bg-purple-600 text-white shadow-sm;
}
.archive-tab-all-idle {
@apply text-stone-600 dark:text-stone-300 hover:bg-stone-200/70 dark:hover:bg-zinc-800;
}
.archive-tab-fresh-active {
@apply bg-amber-500 text-white shadow-sm;
}
.archive-tab-fresh-idle {
@apply text-stone-600 dark:text-stone-300 hover:bg-amber-100/60 dark:hover:bg-amber-950/40;
}
.archive-tab-curated-active {
@apply bg-cyan-600 text-white shadow-sm;
}
.archive-tab-curated-idle {
@apply text-stone-600 dark:text-stone-300 hover:bg-cyan-100/60 dark:hover:bg-cyan-950/40;
}
.archive-tab-top-active {
@apply bg-emerald-600 text-white shadow-sm;
}
.archive-tab-top-idle {
@apply text-stone-600 dark:text-stone-300 hover:bg-emerald-100/60 dark:hover:bg-emerald-950/40;
}
/* Сортировка */
.archive-sort-wrap {
@apply flex items-center gap-2 self-end md:self-auto;
}
.archive-sort-label {
@apply text-xs font-bold text-stone-500 dark:text-stone-400 shrink-0;
}
.archive-sort-select {
@apply text-xs font-medium bg-white dark:bg-zinc-800 text-stone-800 dark:text-stone-200
border border-stone-300 dark:border-zinc-700 rounded-xl px-3 py-1.5
focus:outline-none focus:ring-2 focus:ring-purple-500/50 cursor-pointer shadow-sm;
}
/* Сетка и пустое состояние */
.gallery-grid {
@apply grid grid-cols-2 sm:grid-cols-2 md:grid-cols-4 gap-4 sm:gap-6;
}
.archive-empty {
@apply col-span-full py-16 text-center text-stone-400 space-y-3
bg-stone-100/50 dark:bg-zinc-900/50 rounded-2xl border border-stone-200 dark:border-zinc-800;
}
.archive-empty-icon {
@apply w-12 h-12 mx-auto opacity-30 animate-pulse;
}
.archive-empty-text {
@apply font-medium text-sm;
}
/* Пагинация */
.paginator {
@apply flex items-center justify-between pt-6 border-t border-stone-200 dark:border-zinc-800;
}
.paginator-btn {
@apply inline-flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs font-bold
transition-all border-0 no-underline;
}
.paginator-btn-prev {
@apply bg-stone-100 hover:bg-stone-200 dark:bg-zinc-800 dark:hover:bg-zinc-700
text-stone-700 dark:text-stone-300 hover:scale-105 active:scale-95 shadow-sm
border border-stone-200 dark:border-zinc-700/60 cursor-pointer;
}
.paginator-btn-next {
@apply bg-purple-600 hover:bg-purple-700 text-white hover:scale-105 active:scale-95 shadow-md cursor-pointer;
}
.paginator-btn-disabled {
@apply text-stone-400 dark:text-stone-600 bg-stone-100/50 dark:bg-zinc-900/50
border border-stone-200/50 dark:border-zinc-800/50 opacity-50 cursor-not-allowed;
}
.paginator-pages {
@apply hidden sm:flex items-center gap-1.5 font-mono text-xs;
}
.paginator-page-active {
@apply w-8 h-8 rounded-lg bg-purple-600 text-white font-bold flex items-center justify-center shadow-md;
}
.paginator-page-link {
@apply w-8 h-8 rounded-lg bg-stone-100 hover:bg-stone-200 dark:bg-zinc-800 dark:hover:bg-zinc-700
text-stone-700 dark:text-stone-300 flex items-center justify-center transition-colors border-0 no-underline;
}
}
/* ==========================================================================
ТИПОГРАФИКА И СТИЛИ ДЛЯ СТАТЕЙ БЛОГА (CodeMirror / Rich HTML Content)
========================================================================== */
.prose {
color: #292524;
line-height: 1.75;
font-size: 1.0625rem;
}
.prose > :first-child {
margin-top: 0 !important;
}
.dark .prose {
color: #e7e5e4;
}
/* Заголовки */
.prose h1 {
font-size: 2.25rem;
font-weight: 900;
line-height: 1.2;
letter-spacing: -0.03em;
margin-top: 2rem;
margin-bottom: 1rem;
color: #1c1917;
}
.dark .prose h1 {
color: #f5f5f4;
}
.prose h2 {
font-size: 1.625rem;
font-weight: 800;
line-height: 1.3;
letter-spacing: -0.02em;
margin-top: 2.25rem;
margin-bottom: 0.85rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid rgba(168, 85, 247, 0.25);
color: #1c1917;
}
.dark .prose h2 {
color: #f5f5f4;
border-bottom-color: rgba(168, 85, 247, 0.35);
}
.prose h3 {
font-size: 1.35rem;
font-weight: 700;
line-height: 1.4;
letter-spacing: -0.01em;
margin-top: 1.75rem;
margin-bottom: 0.65rem;
color: #292524;
}
.dark .prose h3 {
color: #e7e5e4;
}
.prose h4 {
font-size: 1.15rem;
font-weight: 700;
line-height: 1.45;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
color: #292524;
}
.dark .prose h4 {
color: #e7e5e4;
}
.prose h5,
.prose h6 {
font-size: 0.95rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 1.25rem;
margin-bottom: 0.4rem;
color: #57534e;
}
.dark .prose h5,
.dark .prose h6 {
color: #a8a29e;
}
/* Параграфы и текстовые элементы */
.prose p {
margin-top: 0;
margin-bottom: 1.25rem;
}
.prose strong,
.prose b {
font-weight: 700;
color: #1c1917;
}
.dark .prose strong,
.dark .prose b {
color: #ffffff;
}
.prose em,
.prose i {
font-style: italic;
}
.prose mark {
background-color: rgba(168, 85, 247, 0.2);
color: #6b21a8;
padding: 0.15rem 0.35rem;
border-radius: 0.25rem;
}
.dark .prose mark {
background-color: rgba(168, 85, 247, 0.35);
color: #e9d5ff;
}
/* Разделители */
.prose hr {
margin: 2.25rem 0;
border: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(168, 85, 247, 0.4), transparent);
}
.dark .prose hr {
background: linear-gradient(90deg, transparent, rgba(168, 85, 247, 0.6), transparent);
}
/* Списки */
.prose ul {
list-style-type: disc;
margin-top: 0.75rem;
margin-bottom: 1.25rem;
padding-left: 1.65rem;
}
.prose ol {
list-style-type: decimal;
margin-top: 0.75rem;
margin-bottom: 1.25rem;
padding-left: 1.65rem;
}
.prose li {
margin-top: 0.4rem;
margin-bottom: 0.4rem;
line-height: 1.65;
}
.prose li::marker {
color: #9333ea;
}
.dark .prose li::marker {
color: #c084fc;
}
.prose ul ul,
.prose ol ol,
.prose ul ol,
.prose ol ul {
margin-top: 0.35rem;
margin-bottom: 0.35rem;
padding-left: 1.35rem;
}
.prose ul ul {
list-style-type: circle;
}
.prose ul ul ul {
list-style-type: square;
}
/* Цитаты */
.prose blockquote {
margin: 1.5rem 0;
padding: 0.85rem 1.25rem;
border-left: 4px solid #a855f7;
background-color: rgba(245, 245, 244, 0.6);
border-radius: 0 1rem 1rem 0;
font-style: italic;
color: #57534e;
}
.dark .prose blockquote {
background-color: rgba(24, 24, 27, 0.6);
color: #d6d3d1;
}
.prose blockquote p:last-child {
margin-bottom: 0;
}
/* Таблицы */
.prose table {
width: 100%;
margin: 1.75rem 0;
border-collapse: collapse;
text-align: left;
font-size: 0.9375rem;
border: 1px solid rgba(228, 228, 231, 0.9);
border-radius: 0.875rem;
overflow: hidden;
display: table;
}
.dark .prose table {
border-color: rgba(63, 63, 70, 0.7);
}
.prose thead {
background-color: rgba(244, 244, 245, 0.95);
border-bottom: 2px solid rgba(212, 212, 216, 0.9);
}
.dark .prose thead {
background-color: rgba(39, 39, 42, 0.95);
border-bottom-color: rgba(63, 63, 70, 0.9);
}
.prose th {
padding: 0.75rem 1rem;
font-weight: 700;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.05em;
color: #18181b;
}
.dark .prose th {
color: #f4f4f5;
}
.prose td {
padding: 0.75rem 1rem;
border-bottom: 1px solid rgba(244, 244, 245, 0.9);
vertical-align: top;
}
.dark .prose td {
border-bottom-color: rgba(39, 39, 42, 0.8);
}
.prose tbody tr:last-child td {
border-bottom: 0;
}
.prose tbody tr:nth-child(even) {
background-color: rgba(250, 250, 250, 0.5);
}
.dark .prose tbody tr:nth-child(even) {
background-color: rgba(24, 24, 27, 0.3);
}
.prose tbody tr:hover {
background-color: rgba(168, 85, 247, 0.05);
}
.dark .prose tbody tr:hover {
background-color: rgba(168, 85, 247, 0.1);
}
.prose caption {
padding: 0.5rem;
font-size: 0.8rem;
font-style: italic;
color: #78716c;
}
/* Код и блоки кода */
.prose code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875em;
background-color: rgba(243, 244, 246, 0.9);
color: #7e22ce;
padding: 0.15rem 0.4rem;
border-radius: 0.375rem;
border: 1px solid rgba(229, 231, 235, 0.8);
font-weight: 600;
}
.dark .prose code {
background-color: rgba(39, 39, 42, 0.8);
color: #d8b4fe;
border-color: rgba(63, 63, 70, 0.8);
}
.prose pre {
margin: 1.5rem 0;
padding: 1.25rem;
border-radius: 1rem;
background-color: #18181b;
color: #f4f4f5;
overflow-x: auto;
border: 1px solid rgba(63, 63, 70, 0.6);
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.2);
}
.prose pre code {
background-color: transparent;
color: inherit;
padding: 0;
border: 0;
font-size: 0.9em;
font-weight: 400;
}
/* Изображения и медиа */
.prose img {
max-width: 100%;
height: auto;
border-radius: 1rem;
margin: 1.5rem auto;
display: block;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}
.prose figure {
margin: 1.75rem 0;
text-align: center;
}
.prose figcaption {
margin-top: 0.5rem;
font-size: 0.85rem;
color: #78716c;
font-style: italic;
}
/* Спойлеры и клавиши */
.prose details {
margin: 1.25rem 0;
padding: 0.85rem 1.15rem;
border-radius: 1rem;
background-color: rgba(245, 245, 244, 0.6);
border: 1px solid rgba(228, 228, 231, 0.8);
}
.dark .prose details {
background-color: rgba(24, 24, 27, 0.6);
border-color: rgba(63, 63, 70, 0.6);
}
.prose summary {
font-weight: 700;
cursor: pointer;
color: #18181b;
user-select: none;
}
.dark .prose summary {
color: #f4f4f5;
}
.prose kbd {
font-family: ui-monospace, monospace;
font-size: 0.8em;
padding: 0.15rem 0.4rem;
border-radius: 0.25rem;
background-color: #e4e4e7;
color: #18181b;
border: 1px solid #d4d4d8;
box-shadow: 0 1px 0 1px #a1a1aa;
}
.dark .prose kbd {
background-color: #27272a;
color: #f4f4f5;
border-color: #3f3f46;
box-shadow: 0 1px 0 1px #18181b;
}