////////////////////////////////////////////////////////////////////////////////
// A1sCSS - Модуль управления CSS для A1sNoCode
// Часть библиотеки A1sCode (a1scode.ru)
// Версия: 2.0
// Дата: 2025
////////////////////////////////////////////////////////////////////////////////
#Область ПрограммныйИнтерфейс
#Область ПолучениеСтилей
// Возвращает полный CSS со всеми современными стилями
// Включает базовые стили, компоненты и темы
//
// Параметры:
// ВключитьШрифты - Булево - включить загрузку веб-шрифтов (по умолчанию Истина)
// Тема - Строка - название темы: "light", "dark", "auto" (по умолчанию "light")
//
// Возвращаемое значение:
// Строка - полный CSS код, готовый к встраиванию в HTML
//
Function GetModernCSS(ВключитьШрифты = Истина, Тема = "light") Export
CSS = "";
// Базовые стили
CSS = CSS + GetBaseStyles();
// Шрифты (опционально)
Если ВключитьШрифты Тогда
CSS = CSS + GetFontStyles();
КонецЕсли;
// Компоненты
CSS = CSS + GetComponentStyles();
// Темы
CSS = CSS + GetThemeStyles(Тема);
// Утилиты
CSS = CSS + GetUtilityStyles();
Возврат "";
КонецФункции
// Возвращает минимальный CSS для быстрой загрузки
// Используется когда нужен только базовый функционал без декоративных элементов
//
// Возвращаемое значение:
// Строка - минимальный CSS код
//
Function GetMinimalCSS() Export
CSS = GetBaseStyles() + Символы.ПС + GetUtilityStyles();
Возврат "";
КонецФункции
// Возвращает CSS только для компонентов
// Используется когда базовые стили уже загружены
//
// Возвращаемое значение:
// Строка - CSS компонентов
//
Function GetComponentsCSS() Export
CSS = GetComponentStyles();
Возврат "";
КонецФункции
#КонецОбласти
#Область ЧтениеФайловСтилей
// Читает содержимое CSS файла из каталога модуля
//
// Параметры:
// ИмяФайла - Строка - имя файла CSS (например "base.css")
//
// Возвращаемое значение:
// Строка - содержимое файла или пустая строка при ошибке
//
Function ReadCSSFile(ИмяФайла) Export
Попытка
// Путь к каталогу модуля (должен быть настроен в конфигурации)
ПутьККаталогу = ПолучитьПутьККаталогуМодуля();
// Объединяем путь с именем файла
Разделитель = ПолучитьРазделительПути();
Если Прав(ПутьККаталогу, 1) <> Разделитель Тогда
ПутьККаталогу = ПутьККаталогу + Разделитель;
КонецЕсли;
ПолныйПуть = ПутьККаталогу + ИмяФайла;
Файл = Новый Файл(ПолныйПуть);
Если Файл.Существует() Тогда
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.Прочитать(ПолныйПуть, КодировкаТекста.UTF8);
Возврат ТекстовыйДокумент.ПолучитьТекст();
КонецЕсли;
Исключение
// Логируем ошибку, но не прерываем выполнение
Попытка
A1sS.Log("A1sCSS.ReadCSSFile: Ошибка чтения файла " + ИмяФайла + ": " + ОписаниеОшибки());
Исключение
// Если A1sS недоступен, просто игнорируем
КонецПопытки;
КонецПопытки;
Возврат "";
КонецФункции
// Возвращает путь к каталогу модуля A1sCSS
// Может быть переопределен через константу или настройки
//
// Возвращаемое значение:
// Строка - путь к каталогу
//
Function ПолучитьПутьККаталогуМодуля() Export
// По умолчанию используем каталог временных файлов
ПутьПоУмолчанию = КаталогВременныхФайлов() + "A1sCSS" + ПолучитьРазделительПути();
Путь = "";
// Пытаемся получить значение константы (если она существует)
Попытка
// Используем метаданные для проверки существования константы
//Для Каждого МетаКонстанта Из Метаданные.Константы Цикл
// Если МетаКонстанта.Имя = "A1sConst_PathToA1sCSSFolder" Тогда
// Try
// //Путь = Константы["A1sConst_PathToA1sCSSFolder"].Получить();
// Если НЕ ПустаяСтрока(Путь) Тогда
// Возврат Путь;
// КонецЕсли;
// Except
// Возврат Путь;
// EndTry;
// КонецЕсли;
//КонецЦикла;
Исключение
// Константа не существует или недоступна - используем путь по умолчанию
КонецПопытки;
Возврат ПутьПоУмолчанию;
КонецФункции
#КонецОбласти
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
#Область БазовыеСтили
Function GetBaseStyles()
CSS = "
|/* === A1sCode Base Styles === */
|
|* {
| box-sizing: border-box;
| padding: 0;
|}
|
|html {
| font-family: 'JetBrains Mono', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
| font-feature-settings: 'kern' 1, 'liga' 1, 'calt' 1;
| text-rendering: optimizeLegibility;
| max-width: 85%;
| margin: auto;
| font-size: calc(13.375px + 0.390625vw);
| line-height: 1.6;
|}
|
|html, body {
| color: #444;
| background: #fff;
| -webkit-font-smoothing: antialiased;
| -moz-osx-font-smoothing: grayscale;
|}
|
|/* Числовые и финансовые данные */
|.financial-data, table, td, th, code, pre,
|.price, .amount, .date, [data-numeric] {
| font-feature-settings: 'tnum' 1, 'zero' 1, 'ss01' 1, 'ss02' 1;
| font-variant-numeric: tabular-nums lining-nums;
|}
|
|/* Flexbox layout для контента и aside */
|flexcontent {
| display: flex;
| flex-wrap: wrap;
| justify-content: center;
| padding: 20px;
| gap: 20px;
|}
|
|main {
| flex: 1;
| min-width: 0;
| padding: 20px;
|}
|
|aside {
| flex: 0 0 300px;
| max-width: 300px;
| padding: 20px;
| border: 1px solid #e5e7eb;
| border-radius: 14px;
| box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
| background-color: #f9fafb;
|}
|
|@media (max-width: 900px) {
| aside {
| flex: 1 1 100%;
| max-width: 100%;
| }
|}
|
|/* Типографика */
|h1, h2, h3, h4, h5, h6 {
| margin: 1.2em 0 0.6em 0;
| font-weight: 700;
| line-height: 1.3;
| text-rendering: optimizeLegibility;
|}
|
|h1 {
| font-size: 2rem;
| color: #1a1a1a;
| border-bottom: 3px solid #ffa34d;
| padding-bottom: 0.3em;
|}
|
|h2 {
| font-size: 1.6rem;
| color: #2d2d2d;
| border-bottom: 2px solid #ffc280;
| padding-bottom: 0.25em;
|}
|
|h3 {
| font-size: 1.3rem;
| color: #3d3d3d;
|}
|
|h4 {
| font-size: 1.1rem;
| color: #4d4d4d;
|}
|
|h5, h6 {
| font-size: 1rem;
| color: #5d5d5d;
|}
|
|p {
| margin: 1em 0;
| max-width: 768px;
| line-height: 1.7;
| word-wrap: break-word;
|}
|
|a {
| color: #3b82f6;
| text-decoration: none;
| transition: all 0.2s ease;
|}
|
|a:hover {
| color: #1d4ed8;
| text-decoration: underline;
|}
|
|blockquote {
| margin: 1.5em 0;
| padding: 1em 1.5em;
| border-left: 5px solid #ffa34d;
| background: rgba(255, 163, 77, 0.05);
| color: rgba(0, 0, 0, 0.7);
| border-radius: 0 8px 8px 0;
|}
|
|code {
| font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
| font-size: 0.9em;
| background: #fff8ef;
| padding: 0.2em 0.4em;
| border-radius: 4px;
| border: 1px solid #ffe1bf;
|}
|
|pre {
| background: #fff8ef;
| border: 1px solid #ffe1bf;
| border-radius: 8px;
| padding: 1em;
| overflow-x: auto;
| margin: 1.5em 0;
|}
|
|pre code {
| background: none;
| border: none;
| padding: 0;
|}
|
|hr {
| border: none;
| border-top: 2px solid #e5e7eb;
| margin: 2em 0;
|}
|
|strong, b {
| color: #ff7d08;
| font-weight: 700;
| background-color: rgba(255, 125, 8, 0.08);
| padding: 0 0.2em;
| border-radius: 3px;
|}
|
|em, i {
| font-style: italic;
| background: #fffbeb;
| padding: 0 0.3em;
| border-radius: 3px;
|}
|
|/* Списки */
|ul, ol {
| margin: 1em 0;
| padding-left: 2em;
|}
|
|li {
| margin: 0.5em 0;
| line-height: 1.6;
|}
|
|/* Таблицы */
|table {
| width: 100%;
| border-collapse: separate;
| border-spacing: 0;
| margin: 1.5em 0;
| border: 1px solid #e5e7eb;
| border-radius: 8px;
| overflow: hidden;
|}
|
|th, td {
| padding: 0.8em 1em;
| text-align: left;
| border-bottom: 1px solid #e5e7eb;
|}
|
|th {
| background: #f9fafb;
| font-weight: 700;
| color: #1f2937;
|}
|
|tr:last-child td {
| border-bottom: none;
|}
|
|tr:hover {
| background: #f9fafb;
|}";
Возврат CSS;
КонецФункции
#КонецОбласти
#Область СтилиШрифтов
Function GetFontStyles()
CSS = "
|
|/* === Web Fonts === */
|
|@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');
|@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&display=swap');";
Возврат CSS;
КонецФункции
#КонецОбласти
#Область СтилиКомпонентов
Function GetComponentStyles()
CSS = "
|
|/* === Components === */
|
|/* Alerts & Callouts */
|.alert, .callout {
| --accent-rgb: 255, 163, 77;
| position: relative;
| margin: 14px 0;
| padding: 12px 14px 12px 16px;
| border-radius: 14px;
| border: 1px solid rgba(var(--accent-rgb), 0.25);
| background: rgba(var(--accent-rgb), 0.06);
|}
|
|.alert::before, .callout::before {
| content: '';
| position: absolute;
| inset: 0 auto 0 0;
| width: 6px;
| border-radius: 14px 0 0 14px;
| background: linear-gradient(180deg,
| rgba(var(--accent-rgb), 0.95),
| rgba(var(--accent-rgb), 0.55));
|}
|
|.alert strong {
| font-weight: 700;
| margin-right: 0.35rem;
|}
|
|.alert a {
| color: rgb(var(--accent-rgb));
| font-weight: 600;
| text-decoration: none;
| border-radius: 6px;
| padding: 1px 3px;
|}
|
|.alert a:hover {
| text-decoration: underline;
| background: rgba(var(--accent-rgb), 0.10);
|}
|
|/* Alert variants */
|.alert--info, .callout--info { --accent-rgb: 59, 130, 246; }
|.alert--warn, .callout--warn { --accent-rgb: 245, 158, 11; }
|.alert--success, .callout--ok { --accent-rgb: 16, 185, 129; }
|.alert--danger, .callout--danger { --accent-rgb: 239, 68, 68; }
|
|/* Cards */
|.card, .lib-card, .content-card {
| background: #fff;
| border: 1px solid #e5e7eb;
| border-radius: 14px;
| padding: 20px;
| margin: 14px 0;
| box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
| transition: transform 0.18s ease, box-shadow 0.18s ease;
|}
|
|.card:hover, .lib-card:hover, .content-card:hover {
| transform: translateY(-3px);
| box-shadow: 0 12px 28px rgba(0, 0, 0, 0.10);
|}
|
|/* Buttons */
|.btn {
| display: inline-block;
| padding: 10px 20px;
| font-weight: 600;
| text-align: center;
| border: none;
| border-radius: 12px;
| cursor: pointer;
| transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease;
| background: linear-gradient(135deg, #ffa34d, #ff7d08);
| color: #fff !important;
| text-decoration: none;
|}
|
|.btn:hover {
| transform: translateY(-1px);
| box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
| filter: brightness(0.98);
|}
|
|.btn:active {
| transform: translateY(0);
| box-shadow: 0 4px 10px rgba(0, 0, 0, 0.10);
| filter: brightness(0.92);
|}
|
|.btn:focus-visible {
| outline: 2px solid rgba(255, 163, 77, 0.9);
| outline-offset: 2px;
|}
|
|.btn.secondary {
| background: #fff;
| color: #ff7d08 !important;
| border: 1px solid #ffa34d;
| box-shadow: none;
|}
|
|.btn.secondary:hover {
| background: #fff8ef;
|}
|
|.btn.secondary:hover,
|.btn.secondary:active {
| filter: none;
|}
|
|/* Pills/Badges */
|.pill, .badge {
| display: inline-block;
| padding: 4px 12px;
| font-size: 0.75rem;
| font-weight: 700;
| border-radius: 999px;
| color: #fff;
| background: linear-gradient(45deg, #ffa34d, #ff7d08);
| box-shadow: 0 4px 14px rgba(255, 125, 8, 0.35);
|}
|
|/* Chips */
|.chip {
| display: inline-flex;
| align-items: center;
| gap: 6px;
| padding: 4px 10px;
| border: 1px solid #e5e7eb;
| border-radius: 999px;
| font-size: 12px;
| background: #fff;
| font-weight: 500;
|}
|
|/* Grid system */
|.grid {
| display: grid;
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
| gap: 20px;
| margin: 20px 0;
|}
|
|/* Code highlighting */
|pre.code {
| background: #fff8ef;
| border: 1px solid #ffe1bf;
|}
|
|pre.code code {
| color: #222;
|}";
Возврат CSS;
КонецФункции
#КонецОбласти
#Область СтилиТемы
Function GetThemeStyles(Тема = "light")
Если Тема = "dark" Тогда
Возврат GetDarkThemeStyles();
ИначеЕсли Тема = "auto" Тогда
Возврат GetAutoThemeStyles();
Иначе
Возврат ""; // Светлая тема по умолчанию уже применена
КонецЕсли;
КонецФункции
Function GetDarkThemeStyles()
CSS = "
|
|/* === Dark Theme === */
|
|html, body {
| color: #e5e7eb;
| background: #0f172a;
|}
|
|h1, h2, h3, h4, h5, h6 {
| color: #f1f5f9;
|}
|
|a {
| color: #60a5fa;
|}
|
|a:hover {
| color: #93c5fd;
|}
|
|code, pre {
| background: #1e293b;
| border-color: #334155;
|}
|
|aside {
| background: #1e293b;
| border-color: #334155;
|}
|
|.card, .lib-card, .content-card {
| background: #1e293b;
| border-color: #334155;
|}
|
|table {
| border-color: #334155;
|}
|
|th {
| background: #1e293b;
| color: #f1f5f9;
|}
|
|td {
| border-color: #334155;
|}
|
|tr:hover {
| background: #1e293b;
|}";
Возврат CSS;
КонецФункции
Function GetAutoThemeStyles()
CSS = "
|
|/* === Auto Theme (respects system preference) === */
|
|@media (prefers-color-scheme: dark) {
| html, body {
| color: #e5e7eb;
| background: #0f172a;
| }
|
| h1, h2, h3, h4, h5, h6 {
| color: #f1f5f9;
| }
|
| a {
| color: #60a5fa;
| }
|
| a:hover {
| color: #93c5fd;
| }
|
| code, pre {
| background: #1e293b;
| border-color: #334155;
| }
|
| aside {
| background: #1e293b;
| border-color: #334155;
| }
|
| .card, .lib-card, .content-card {
| background: #1e293b;
| border-color: #334155;
| }
|}";
Возврат CSS;
КонецФункции
#КонецОбласти
#Область УтилитныеСтили
Function GetUtilityStyles()
CSS = "
|
|/* === Utility Classes === */
|
|/* Text sizes */
|.text-xs { font-size: 0.75rem; }
|.text-sm { font-size: 0.875rem; }
|.text-base { font-size: 1rem; }
|.text-lg { font-size: 1.125rem; }
|.text-xl { font-size: 1.25rem; }
|.text-2xl { font-size: 1.5rem; }
|
|/* Colors */
|.text-primary { color: #ffa34d; }
|.text-secondary { color: #6b7280; }
|.text-success { color: #10b981; }
|.text-warning { color: #f59e0b; }
|.text-danger { color: #ef4444; }
|.text-info { color: #3b82f6; }
|
|/* Backgrounds */
|.bg-primary { background-color: #ffa34d; }
|.bg-light { background-color: #f9fafb; }
|.bg-dark { background-color: #1f2937; }
|
|/* Spacing */
|.mt-1 { margin-top: 0.5rem; }
|.mt-2 { margin-top: 1rem; }
|.mt-3 { margin-top: 1.5rem; }
|.mt-4 { margin-top: 2rem; }
|
|.mb-1 { margin-bottom: 0.5rem; }
|.mb-2 { margin-bottom: 1rem; }
|.mb-3 { margin-bottom: 1.5rem; }
|.mb-4 { margin-bottom: 2rem; }
|
|.p-1 { padding: 0.5rem; }
|.p-2 { padding: 1rem; }
|.p-3 { padding: 1.5rem; }
|.p-4 { padding: 2rem; }
|
|/* Display */
|.hidden { display: none; }
|.block { display: block; }
|.inline { display: inline; }
|.inline-block { display: inline-block; }
|.flex { display: flex; }
|.grid { display: grid; }
|
|/* Text alignment */
|.text-left { text-align: left; }
|.text-center { text-align: center; }
|.text-right { text-align: right; }
|
|/* Font weight */
|.font-normal { font-weight: 400; }
|.font-medium { font-weight: 500; }
|.font-semibold { font-weight: 600; }
|.font-bold { font-weight: 700; }
|
|/* Focus & accessibility */
|a:focus-visible,
|.btn:focus-visible,
|button:focus-visible {
| outline: 2px solid #94a3b8;
| outline-offset: 3px;
| border-radius: 6px;
|}";
Возврат CSS;
КонецФункции
#КонецОбласти
#КонецОбласти