diff --git a/.eleventy.js b/.eleventy.js index 181edac..43801fb 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -3,6 +3,10 @@ module.exports = function (eleventyConfig) { eleventyConfig.addPassthroughCopy('src/assets'); eleventyConfig.addPassthroughCopy({ 'node_modules/@ce/transliteration/translit.js': 'assets/repositories/chechen-transliterator/translit.js'}); + // Copy SEO files + eleventyConfig.addPassthroughCopy('src/robots.txt'); + eleventyConfig.addPassthroughCopy('src/sitemap.xml'); + return { dir: { input: 'src', diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d1f59cf --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,66 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a static website for the Chechen Language organization, built with Eleventy (11ty). The site showcases various Chechen language tools and projects, including keyboard layouts for different platforms and a Chechen transliterator tool. + +## Build and Development Commands + +- `npm run build` - Build the static site (output to `_site/`) +- `npm run serve` - Start development server with live reload +- `npm run prettier` - Format all files with Prettier + +## Architecture + +### Static Site Generation (Eleventy) + +The site uses Eleventy with the following structure: +- Input directory: `src/` +- Output directory: `_site/` +- Templates: `src/_includes/` (Nunjucks templates) + +### Configuration (.eleventy.js) + +- Base layout template: `src/_includes/layout.njk` +- Static assets are copied from `src/assets/` to output +- The `@ce/transliteration` package is copied from node_modules to `_site/assets/repositories/chechen-transliterator/translit.js` + +### Content Organization + +- `src/index.md` - Home page +- `src/repositories/` - Individual project pages (markdown files) + - `chechen-transliterator/` - Interactive transliterator tool + - `chechen-latin-keyboard-{platform}/` - Platform-specific keyboard documentation + - Includes privacy policy pages for Android app + +### Layout System + +- Single base template: `src/_includes/layout.njk` +- Uses Bootstrap 4.5.2 for styling +- Responsive navigation with dropdown menu for projects +- Sticky footer layout + +### Interactive Features + +The transliterator page (`src/repositories/chechen-transliterator/index.md`) has special functionality: +- Uses `@ce/transliteration` package (JSR package: `@jsr/ce__transliteration`) +- Client-side JavaScript: `src/assets/repositories/chechen-transliterator/main.js` +- Handles URL query parameters to pre-fill and transliterate text +- Syncs input text to URL query string for sharing/bookmarking + +### Special Handling + +- The transliteration logic handles the letter 'н' at word endings with blacklist and unsure list checking +- Unsure words get flagged with 'ŋ[REPLACE]' for manual review + +## Dependencies + +- Core: `@11ty/eleventy` (static site generator) +- Formatting: `prettier` with `lint-staged` and `husky` for pre-commit hooks +- Runtime: `@ce/transliteration` (Chechen transliteration logic) + +## Deployment + +This is a GitHub Pages site (indicated by `.nojekyll` file). The built site from `_site/` is deployed to GitHub Pages. diff --git a/src/_includes/layout.njk b/src/_includes/layout.njk index a58f5dd..87537f5 100644 --- a/src/_includes/layout.njk +++ b/src/_includes/layout.njk @@ -3,59 +3,164 @@ - {{ title }} - + + + {{ title }} | Chechen Language Projects + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - {% block extra_head %} {% endblock %} - -
-
-

Chechen Language Projects

- -
-
-
-
- {{ content | safe }} + +
+
+ + + +
+
+ {{ content | safe }} +
+ +
+ + +
+

© 2024 Chechen Language Projects

+
+
+ +
-
-
-

© 2024 Chechen Language Projects

-
-
+ +
+ + + {% block extra_scripts %} {% endblock %} - \ No newline at end of file + diff --git a/src/assets/css/components/buttons.css b/src/assets/css/components/buttons.css new file mode 100644 index 0000000..061c51a --- /dev/null +++ b/src/assets/css/components/buttons.css @@ -0,0 +1,92 @@ +/* Button component */ + +.btn { + display: inline-block; + padding: var(--space-sm) var(--space-lg); + font-family: var(--font-sans); + font-size: var(--text-base); + font-weight: var(--font-medium); + text-decoration: none; + border-radius: var(--radius-sm); + border: var(--border-thin) solid transparent; + cursor: pointer; + transition: all var(--transition-base); + position: relative; + text-align: center; +} + +.btn:focus { + outline: 2px solid var(--color-gold); + outline-offset: 2px; +} + +/* Primary button */ +.btn-primary { + background-color: var(--color-burgundy); + color: var(--color-gold); + border-color: var(--color-burgundy-dark); +} + +.btn-primary:hover, +.btn-primary:focus { + background-color: var(--color-gold); + color: var(--color-burgundy); + border-color: var(--color-burgundy); + box-shadow: var(--shadow-md); + text-decoration: none; +} + +/* Secondary button */ +.btn-secondary { + background-color: var(--color-forest); + color: var(--color-gold-light); + border-color: var(--color-forest-dark); +} + +.btn-secondary:hover, +.btn-secondary:focus { + background-color: var(--color-forest-light); + color: var(--color-white); + border-color: var(--color-forest); + box-shadow: var(--shadow-md); + text-decoration: none; +} + +/* Outline button */ +.btn-outline { + background-color: transparent; + color: var(--color-burgundy); + border-color: var(--color-burgundy); +} + +.btn-outline:hover, +.btn-outline:focus { + background-color: var(--color-burgundy); + color: var(--color-gold); + text-decoration: none; +} + +/* Button sizes */ +.btn-sm { + padding: var(--space-xs) var(--space-md); + font-size: var(--text-sm); +} + +.btn-lg { + padding: var(--space-md) var(--space-xl); + font-size: var(--text-lg); +} + +/* Full width button */ +.btn-block { + display: block; + width: 100%; +} + +/* Disabled state */ +.btn:disabled, +.btn[disabled] { + opacity: 0.6; + cursor: not-allowed; + pointer-events: none; +} diff --git a/src/assets/css/components/cards.css b/src/assets/css/components/cards.css new file mode 100644 index 0000000..026d73c --- /dev/null +++ b/src/assets/css/components/cards.css @@ -0,0 +1,91 @@ +/* Card component for repository listings */ + +.card { + background-color: var(--color-bg-light); + border: var(--border-medium) solid var(--color-burgundy); + border-radius: var(--radius-md); + padding: var(--space-xl); + position: relative; + transition: all var(--transition-base); + margin-bottom: var(--space-lg); +} + +.card::before { + content: ''; + position: absolute; + top: -15px; + left: 50%; + transform: translateX(-50%); + width: 60px; + height: 30px; + background-image: url('/assets/images/patterns/divider-pattern.svg'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +.card:hover { + border-color: var(--color-gold); + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.card-title { + color: var(--color-burgundy); + font-family: var(--font-serif); + font-size: var(--text-xl); + margin-bottom: var(--space-sm); + font-weight: var(--font-bold); +} + +.card-description { + color: var(--color-text-secondary); + margin-bottom: var(--space-md); + line-height: var(--leading-normal); +} + +.card-link { + color: var(--color-forest); + text-decoration: none; + font-weight: var(--font-medium); + border-bottom: 1px solid transparent; + transition: border-color var(--transition-fast); + display: inline-block; +} + +.card-link:hover, +.card-link:focus { + border-bottom-color: var(--color-forest); + text-decoration: none; +} + +/* Content section wrapper */ +.content-section { + background-color: var(--color-bg-light); + border: var(--border-medium) solid var(--color-burgundy); + border-radius: var(--radius-md); + padding: var(--space-2xl); + margin-bottom: var(--space-xl); +} + +.content-section h1:first-child, +.content-section h2:first-child, +.content-section h3:first-child { + margin-top: 0; +} + +/* Responsive cards */ +@media (max-width: 575px) { + .card { + padding: var(--space-lg); + } + + .card::before { + width: 40px; + height: 20px; + } + + .content-section { + padding: var(--space-lg); + } +} diff --git a/src/assets/css/components/footer.css b/src/assets/css/components/footer.css new file mode 100644 index 0000000..7ecc000 --- /dev/null +++ b/src/assets/css/components/footer.css @@ -0,0 +1,50 @@ +/* Footer component with istang design */ + +.site-footer { + background: linear-gradient(180deg, + var(--color-burgundy-dark) 0%, + var(--color-burgundy) 100%); + border-top: var(--border-medium) solid var(--color-gold); + color: var(--color-gold-light); + padding: var(--space-xl) 0; + min-height: var(--footer-height); + position: relative; + text-align: center; + margin-top: auto; +} + +.site-footer .container { + position: relative; + z-index: var(--z-base); + margin-bottom: 44px; +} + +.site-footer p { + margin: 0; + font-size: var(--text-base); + color: var(--color-gold-light); +} + +.site-footer a { + color: var(--color-gold); + text-decoration: none; + transition: color var(--transition-fast); +} + +.site-footer a:hover, +.site-footer a:focus { + color: var(--color-gold-light); + text-decoration: underline; +} + +/* Responsive footer */ +@media (max-width: 575px) { + .site-footer { + padding: var(--space-lg) 0; + min-height: 60px; + } + + .site-footer p { + font-size: var(--text-sm); + } +} diff --git a/src/assets/css/components/forms.css b/src/assets/css/components/forms.css new file mode 100644 index 0000000..9c12724 --- /dev/null +++ b/src/assets/css/components/forms.css @@ -0,0 +1,148 @@ +/* Form component styles */ + +textarea, +input[type="text"], +input[type="email"], +input[type="password"], +input[type="search"] { + width: 100%; + padding: var(--space-md); + font-family: var(--font-sans); + font-size: var(--text-base); + color: var(--color-text-primary); + background-color: var(--color-white); + border: var(--border-thin) solid var(--color-burgundy); + border-radius: var(--radius-sm); + transition: border-color var(--transition-base), box-shadow var(--transition-base); +} + +textarea:focus, +input[type="text"]:focus, +input[type="email"]:focus, +input[type="password"]:focus, +input[type="search"]:focus { + outline: none; + border-color: var(--color-gold); + box-shadow: 0 0 0 3px rgba(212, 165, 116, 0.2); +} + +textarea { + resize: vertical; + min-height: 120px; +} + +/* Istang-styled textarea for special pages */ +.istang-textarea { + border: var(--border-medium) solid var(--color-burgundy); + background-color: var(--color-bg-light); + padding: var(--space-lg); + border-radius: var(--radius-md); +} + +.istang-textarea:focus { + border-color: var(--color-gold); + background-color: var(--color-white); +} + +/* Form groups */ +.form-group { + margin-bottom: var(--space-lg); +} + +.form-label { + display: block; + margin-bottom: var(--space-sm); + font-weight: var(--font-medium); + color: var(--color-text-primary); +} + +.form-help { + display: block; + margin-top: var(--space-xs); + font-size: var(--text-sm); + color: var(--color-text-secondary); +} + +/* Form validation states */ +.form-error { + border-color: #DC3545; +} + +.form-error:focus { + box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.2); +} + +.form-success { + border-color: #28A745; +} + +.form-success:focus { + box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.2); +} + +.error-message { + color: #DC3545; + font-size: var(--text-sm); + margin-top: var(--space-xs); +} + +.success-message { + color: #28A745; + font-size: var(--text-sm); + margin-top: var(--space-xs); +} + +/* Placeholder styling */ +::placeholder { + color: var(--color-text-secondary); + opacity: 0.7; +} + +/* Form control spacing */ +.form-control { + margin-bottom: var(--space-md); +} + +/* Select styling */ +select { + width: 100%; + padding: var(--space-md); + font-family: var(--font-sans); + font-size: var(--text-base); + color: var(--color-text-primary); + background-color: var(--color-white); + border: var(--border-thin) solid var(--color-burgundy); + border-radius: var(--radius-sm); + cursor: pointer; + transition: border-color var(--transition-base); +} + +select:focus { + outline: none; + border-color: var(--color-gold); + box-shadow: 0 0 0 3px rgba(212, 165, 116, 0.2); +} + +/* Checkbox and radio styling */ +input[type="checkbox"], +input[type="radio"] { + width: auto; + margin-right: var(--space-sm); + cursor: pointer; +} + +/* Responsive forms */ +@media (max-width: 575px) { + textarea, + input[type="text"], + input[type="email"], + input[type="password"], + input[type="search"], + select { + font-size: var(--text-base); + } + + .istang-textarea { + padding: var(--space-md); + } +} diff --git a/src/assets/css/components/header.css b/src/assets/css/components/header.css new file mode 100644 index 0000000..adc5108 --- /dev/null +++ b/src/assets/css/components/header.css @@ -0,0 +1,48 @@ +/* Header component with istang design */ + +.site-header { + background: linear-gradient(180deg, + var(--color-burgundy-dark) 0%, + var(--color-burgundy) 100%); + border-bottom: var(--border-medium) solid var(--color-gold); + position: relative; + padding: var(--space-lg) 0; + min-height: var(--header-height); + overflow: visible; +} + +.site-header .container { + position: relative; + margin-top: 44px; +} + +.site-header h1 { + color: var(--color-gold); + font-size: var(--text-3xl); + margin: 0 0 var(--space-md) 0; + text-align: center; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +/* Responsive header sizing */ +@media (min-width: 768px) { + .site-header h1 { + font-size: var(--text-4xl); + } +} + +@media (max-width: 575px) { + .site-header { + padding: var(--space-md) 0; + min-height: 80px; + } + + .site-header h1 { + font-size: var(--text-lg); + margin-bottom: var(--space-sm); + max-width: 200px; + margin-left: auto; + margin-right: auto; + line-height: var(--leading-tight); + } +} diff --git a/src/assets/css/components/navigation.css b/src/assets/css/components/navigation.css new file mode 100644 index 0000000..c4fe850 --- /dev/null +++ b/src/assets/css/components/navigation.css @@ -0,0 +1,226 @@ +/* Navigation component */ + +.nav { + display: flex; + justify-content: center; + align-items: center; + gap: var(--space-lg); + flex-wrap: wrap; +} + +.nav-link { + color: var(--color-gold-light); + text-decoration: none; + padding: var(--space-sm) var(--space-md); + border-bottom: 2px solid transparent; + transition: all var(--transition-base); + font-weight: var(--font-medium); + font-size: var(--text-base); +} + +.nav-link:hover, +.nav-link:focus { + color: var(--color-gold); + border-bottom-color: var(--color-gold); + text-decoration: none; +} + +/* Dropdown menu */ +.nav-dropdown { + position: relative; +} + +.dropdown-toggle { + cursor: pointer; + background: none; + border: none; + color: var(--color-gold-light); + padding: var(--space-sm) var(--space-md); + border-bottom: 2px solid transparent; + transition: all var(--transition-base); + font-weight: var(--font-medium); + font-size: var(--text-base); + display: flex; + align-items: center; + gap: var(--space-xs); +} + +.dropdown-toggle::after { + content: '▼'; + font-size: var(--text-xs); + transition: transform var(--transition-base); +} + +.dropdown-toggle:hover, +.dropdown-toggle:focus { + color: var(--color-gold); + border-bottom-color: var(--color-gold); +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + background-color: var(--color-bg-cream); + border: var(--border-medium) solid var(--color-burgundy); + border-radius: var(--radius-sm); + padding: var(--space-sm); + box-shadow: var(--shadow-lg); + min-width: 250px; + opacity: 0; + visibility: hidden; + transition: opacity var(--transition-base), visibility var(--transition-base); + z-index: var(--z-dropdown); + margin-top: var(--space-sm); +} + +.nav-dropdown:hover .dropdown-menu, +.nav-dropdown:focus-within .dropdown-menu { + opacity: 1; + visibility: visible; +} + +.dropdown-toggle:hover::after, +.nav-dropdown:hover .dropdown-toggle::after { + transform: rotate(180deg); +} + +.dropdown-item { + display: block; + color: var(--color-text-primary); + padding: var(--space-sm) var(--space-md); + text-decoration: none; + transition: background-color var(--transition-fast); + border-radius: var(--radius-sm); +} + +.dropdown-item:hover, +.dropdown-item:focus { + background-color: var(--color-gold-light); + color: var(--color-burgundy); + text-decoration: none; +} + +/* Mobile navigation */ +.mobile-menu-toggle { + display: none; + background: none; + border: none; + color: var(--color-gold); + font-size: var(--text-2xl); + cursor: pointer; + padding: var(--space-sm); + position: absolute; + right: var(--container-padding); + top: 50%; + transform: translateY(-50%); +} + +.mobile-menu-toggle:hover { + color: var(--color-gold-light); +} + +/* Mobile close button - hidden by default */ +.mobile-close { + display: none; +} + +/* Responsive navigation */ +@media (max-width: 767px) { + .mobile-menu-toggle { + display: block; + } + + .nav { + position: fixed; + top: 0; + right: -100%; + width: 280px; + height: 100vh; + background: linear-gradient(180deg, + var(--color-burgundy-dark) 0%, + var(--color-burgundy) 100%); + flex-direction: column; + justify-content: flex-start; + align-items: stretch; + padding: var(--space-2xl) var(--space-lg); + box-shadow: var(--shadow-lg); + transition: right var(--transition-base); + z-index: var(--z-fixed); + gap: var(--space-md); + pointer-events: auto; + overflow-y: auto; + } + + .nav.active { + right: 0; + } + + .nav-link { + border-bottom: 1px solid var(--color-burgundy-light); + padding: var(--space-md); + } + + .nav-dropdown { + width: 100%; + } + + .dropdown-toggle { + width: 100%; + justify-content: space-between; + border-bottom: 1px solid var(--color-burgundy-light); + padding: var(--space-md); + } + + .dropdown-menu { + position: static; + transform: none; + opacity: 1; + visibility: visible; + margin-top: var(--space-xs); + background-color: var(--color-burgundy-light); + border-color: var(--color-gold); + } + + .dropdown-item { + color: var(--color-gold-light); + } + + .dropdown-item:hover, + .dropdown-item:focus { + background-color: var(--color-burgundy-dark); + color: var(--color-gold); + } + + /* Mobile menu close button */ + .mobile-close { + display: block; + position: absolute; + top: var(--space-md); + right: var(--space-md); + background: none; + border: none; + color: var(--color-gold); + font-size: var(--text-3xl); + cursor: pointer; + padding: var(--space-sm); + line-height: 1; + } +} + +/* Mobile menu overlay */ +.mobile-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: calc(var(--z-fixed) - 1); +} + +.mobile-overlay.active { + display: block; +} diff --git a/src/assets/css/layout.css b/src/assets/css/layout.css new file mode 100644 index 0000000..164c77b --- /dev/null +++ b/src/assets/css/layout.css @@ -0,0 +1,133 @@ +/* Layout system */ + +.istang-layout { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +.istang-border-wrapper { + flex: 1; + display: flex; + flex-direction: column; + overflow: visible; +} + +.container { + width: 100%; + max-width: var(--container-max-width); + margin-left: auto; + margin-right: auto; + padding-left: var(--container-padding); + padding-right: var(--container-padding); +} + +/* Main content area */ +.site-main { + flex: 1; + padding: var(--space-2xl) 0; +} + +/* Grid system */ +.grid { + display: grid; + gap: var(--space-lg); +} + +.grid-cols-1 { + grid-template-columns: repeat(1, 1fr); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, 1fr); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, 1fr); +} + +/* Flexbox utilities */ +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.items-start { + align-items: flex-start; +} + +.items-end { + align-items: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-end { + justify-content: flex-end; +} + +.gap-xs { + gap: var(--space-xs); +} + +.gap-sm { + gap: var(--space-sm); +} + +.gap-md { + gap: var(--space-md); +} + +.gap-lg { + gap: var(--space-lg); +} + +.gap-xl { + gap: var(--space-xl); +} + +/* Responsive grid - mobile first */ +@media (min-width: 576px) { + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 768px) { + .md\:grid-cols-2 { + grid-template-columns: repeat(2, 1fr); + } + .md\:grid-cols-3 { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (min-width: 992px) { + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, 1fr); + } + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, 1fr); + } +} diff --git a/src/assets/css/patterns.css b/src/assets/css/patterns.css new file mode 100644 index 0000000..fda8a93 --- /dev/null +++ b/src/assets/css/patterns.css @@ -0,0 +1,237 @@ +/* SVG Pattern utilities for istang design elements */ + +/* Istang border decorations */ +.istang-border { + position: relative; +} + +.istang-border-top { + height: 96px; + background-color: #475d30; + border-top: var(--border-medium) solid var(--color-gold); + border-bottom: var(--border-medium) solid var(--color-gold); +} + +.istang-border-top::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 88px; + background-image: url('/assets/images/patterns/spiral-border.svg'); + background-repeat: repeat-x; + background-size: 376px 88px; + background-position: center top; + pointer-events: none; +} + +.istang-border-bottom { + height: 96px; + background-color: #475e30; + border-top: var(--border-medium) solid var(--color-gold); + border-bottom: var(--border-medium) solid var(--color-gold); +} + +.istang-border-bottom::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 88px; + background-image: url('/assets/images/patterns/spiral-border.svg'); + background-repeat: repeat-x; + background-size: 376px 88px; + background-position: center bottom; + pointer-events: none; +} + +/* Corner ornaments */ +.istang-corner { + position: absolute; + width: 80px; + height: 80px; + background-size: contain; + background-repeat: no-repeat; + pointer-events: none; + z-index: var(--z-base); +} + +.istang-corner-tl { + top: 32px; + left: 32px; + background-image: url('/assets/images/block-min.png'); +} + +.istang-corner-tr { + top: 32px; + right: 32px; + background-image: url('/assets/images/block-min.png'); + transform: scaleX(-1); +} + +.istang-corner-bl { + bottom: 32px; + left: 32px; + background-image: url('/assets/images/block-min.png'); + transform: scaleY(-1); +} + +.istang-corner-br { + bottom: 32px; + right: 32px; + background-image: url('/assets/images/block-min.png'); + transform: scale(-1, -1); +} + +/* Divider pattern */ +.istang-divider { + position: relative; + text-align: center; + margin: var(--space-xl) 0; +} + +.istang-divider::after { + content: ''; + display: block; + width: 400px; + max-width: 100%; + height: 30px; + margin: var(--space-md) auto 0; + background-image: url('/assets/images/patterns/divider-pattern.svg'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +/* Page title with divider */ +.page-title { + color: var(--color-burgundy); + font-family: var(--font-serif); + font-size: var(--text-4xl); + text-align: center; + margin-bottom: var(--space-xl); + position: relative; + padding-bottom: var(--space-lg); +} + +.page-title::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 200px; + max-width: 90%; + height: 30px; + background-image: url('/assets/images/patterns/divider-pattern.svg'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +/* Responsive adjustments for patterns */ +@media (max-width: 575px) { + /* Smaller spiral borders on mobile */ + .istang-border-top { + height: 48px; + } + + .istang-border-top::before { + height: 44px; + background-size: 188px 44px; + } + + .istang-border-bottom { + height: 48px; + } + + .istang-border-bottom::after { + height: 44px; + background-size: 188px 44px; + } + + /* Smaller corner ornaments on mobile */ + .istang-corner { + width: 40px; + height: 40px; + } + + .istang-corner-tl { + top: 16px; + left: 16px; + } + + .istang-corner-tr { + top: 16px; + right: 16px; + } + + .istang-corner-bl { + bottom: 16px; + left: 16px; + } + + .istang-corner-br { + bottom: 16px; + right: 16px; + } + + /* Smaller page title on mobile */ + .page-title { + font-size: var(--text-2xl); + padding-bottom: var(--space-md); + } + + .page-title::after { + width: 150px; + } +} + +@media (min-width: 576px) and (max-width: 991px) { + /* Medium-sized spiral borders on tablets */ + .istang-border-top { + height: 72px; + } + + .istang-border-top::before { + height: 66px; + background-size: 282px 66px; + } + + .istang-border-bottom { + height: 72px; + } + + .istang-border-bottom::after { + height: 66px; + background-size: 282px 66px; + } + + /* Medium corner ornaments on tablets */ + .istang-corner { + width: 48px; + height: 48px; + } + + .istang-corner-tl { + top: 18px; + left: 18px; + } + + .istang-corner-tr { + top: 18px; + right: 18px; + } + + .istang-corner-bl { + bottom: 18px; + left: 18px; + } + + .istang-corner-br { + bottom: 18px; + right: 18px; + } +} diff --git a/src/assets/css/reset.css b/src/assets/css/reset.css new file mode 100644 index 0000000..1f5b5bb --- /dev/null +++ b/src/assets/css/reset.css @@ -0,0 +1,49 @@ +/* Minimal modern CSS reset */ + +*, *::before, *::after { + box-sizing: border-box; +} + +* { + margin: 0; + padding: 0; +} + +html { + -webkit-text-size-adjust: 100%; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + min-height: 100vh; + line-height: 1.5; +} + +img, picture, video, canvas, svg { + display: block; + max-width: 100%; +} + +input, button, textarea, select { + font: inherit; +} + +p, h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; +} + +ul, ol { + list-style: none; +} + +a { + text-decoration: none; + color: inherit; +} + +button { + background: none; + border: none; + cursor: pointer; +} diff --git a/src/assets/css/typography.css b/src/assets/css/typography.css new file mode 100644 index 0000000..3b1a916 --- /dev/null +++ b/src/assets/css/typography.css @@ -0,0 +1,131 @@ +/* Typography system */ + +body { + font-family: var(--font-sans); + font-size: var(--text-base); + line-height: var(--leading-normal); + color: var(--color-text-primary); + background-color: var(--color-bg-cream); +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-serif); + font-weight: var(--font-bold); + line-height: var(--leading-tight); + color: var(--color-burgundy); + margin-bottom: var(--space-md); +} + +h1 { + font-size: var(--text-4xl); + margin-bottom: var(--space-lg); +} + +h2 { + font-size: var(--text-3xl); +} + +h3 { + font-size: var(--text-2xl); +} + +h4 { + font-size: var(--text-xl); +} + +h5, h6 { + font-size: var(--text-lg); +} + +p { + margin-bottom: var(--space-md); +} + +a { + color: var(--color-forest); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover, a:focus { + color: var(--color-forest-light); + text-decoration: underline; +} + +strong, b { + font-weight: var(--font-bold); +} + +em, i { + font-style: italic; +} + +small { + font-size: var(--text-sm); +} + +code, pre { + font-family: 'Courier New', Courier, monospace; + font-size: var(--text-sm); + background-color: var(--color-bg-light); + padding: 0.125rem 0.25rem; + border-radius: var(--radius-sm); +} + +pre { + padding: var(--space-md); + overflow-x: auto; + border: 1px solid var(--color-gold); +} + +blockquote { + border-left: var(--border-medium) solid var(--color-gold); + padding-left: var(--space-lg); + margin: var(--space-lg) 0; + font-style: italic; + color: var(--color-text-secondary); +} + +ul, ol { + margin-bottom: var(--space-md); + padding-left: var(--space-xl); +} + +ul { + list-style: disc; +} + +ol { + list-style: decimal; +} + +li { + margin-bottom: var(--space-sm); +} + +/* Responsive typography - smaller headings on mobile */ +@media (max-width: 575px) { + h1 { + font-size: var(--text-2xl); + } + + h2 { + font-size: var(--text-xl); + } + + h3 { + font-size: var(--text-lg); + } + + h4 { + font-size: var(--text-base); + } + + h5, h6 { + font-size: var(--text-base); + } + + .page-title { + font-size: var(--text-2xl); + } +} diff --git a/src/assets/css/utilities.css b/src/assets/css/utilities.css new file mode 100644 index 0000000..e933cab --- /dev/null +++ b/src/assets/css/utilities.css @@ -0,0 +1,136 @@ +/* Utility classes */ + +/* Spacing utilities */ +.m-0 { margin: 0; } +.mt-0 { margin-top: 0; } +.mb-0 { margin-bottom: 0; } +.ml-0 { margin-left: 0; } +.mr-0 { margin-right: 0; } + +.m-xs { margin: var(--space-xs); } +.m-sm { margin: var(--space-sm); } +.m-md { margin: var(--space-md); } +.m-lg { margin: var(--space-lg); } +.m-xl { margin: var(--space-xl); } + +.mt-xs { margin-top: var(--space-xs); } +.mt-sm { margin-top: var(--space-sm); } +.mt-md { margin-top: var(--space-md); } +.mt-lg { margin-top: var(--space-lg); } +.mt-xl { margin-top: var(--space-xl); } +.mt-2xl { margin-top: var(--space-2xl); } +.mt-3xl { margin-top: var(--space-3xl); } + +.mb-xs { margin-bottom: var(--space-xs); } +.mb-sm { margin-bottom: var(--space-sm); } +.mb-md { margin-bottom: var(--space-md); } +.mb-lg { margin-bottom: var(--space-lg); } +.mb-xl { margin-bottom: var(--space-xl); } +.mb-2xl { margin-bottom: var(--space-2xl); } +.mb-3xl { margin-bottom: var(--space-3xl); } + +.p-0 { padding: 0; } +.p-xs { padding: var(--space-xs); } +.p-sm { padding: var(--space-sm); } +.p-md { padding: var(--space-md); } +.p-lg { padding: var(--space-lg); } +.p-xl { padding: var(--space-xl); } +.p-2xl { padding: var(--space-2xl); } + +.pt-xs { padding-top: var(--space-xs); } +.pt-sm { padding-top: var(--space-sm); } +.pt-md { padding-top: var(--space-md); } +.pt-lg { padding-top: var(--space-lg); } +.pt-xl { padding-top: var(--space-xl); } + +.pb-xs { padding-bottom: var(--space-xs); } +.pb-sm { padding-bottom: var(--space-sm); } +.pb-md { padding-bottom: var(--space-md); } +.pb-lg { padding-bottom: var(--space-lg); } +.pb-xl { padding-bottom: var(--space-xl); } + +.px-md { padding-left: var(--space-md); padding-right: var(--space-md); } +.px-lg { padding-left: var(--space-lg); padding-right: var(--space-lg); } +.py-md { padding-top: var(--space-md); padding-bottom: var(--space-md); } +.py-lg { padding-top: var(--space-lg); padding-bottom: var(--space-lg); } + +/* Display utilities */ +.block { display: block; } +.inline-block { display: inline-block; } +.inline { display: inline; } +.hidden { display: none; } + +/* Text utilities */ +.text-left { text-align: left; } +.text-center { text-align: center; } +.text-right { text-align: right; } + +.text-xs { font-size: var(--text-xs); } +.text-sm { font-size: var(--text-sm); } +.text-base { font-size: var(--text-base); } +.text-lg { font-size: var(--text-lg); } +.text-xl { font-size: var(--text-xl); } +.text-2xl { font-size: var(--text-2xl); } +.text-3xl { font-size: var(--text-3xl); } + +.font-normal { font-weight: var(--font-normal); } +.font-medium { font-weight: var(--font-medium); } +.font-semibold { font-weight: var(--font-semibold); } +.font-bold { font-weight: var(--font-bold); } + +.text-burgundy { color: var(--color-burgundy); } +.text-gold { color: var(--color-gold); } +.text-forest { color: var(--color-forest); } +.text-white { color: var(--color-white); } + +/* Background utilities */ +.bg-burgundy { background-color: var(--color-burgundy); } +.bg-gold { background-color: var(--color-gold); } +.bg-forest { background-color: var(--color-forest); } +.bg-cream { background-color: var(--color-bg-cream); } +.bg-light { background-color: var(--color-bg-light); } + +/* Width utilities */ +.w-full { width: 100%; } +.w-auto { width: auto; } + +/* Responsive utilities */ +@media (max-width: 575px) { + .sm-hidden { display: none; } +} + +@media (min-width: 576px) { + .sm-block { display: block; } +} + +@media (min-width: 768px) { + .md-block { display: block; } +} + +@media (min-width: 992px) { + .lg-block { display: block; } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/* Reduced motion */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/src/assets/css/variables.css b/src/assets/css/variables.css new file mode 100644 index 0000000..3f112c9 --- /dev/null +++ b/src/assets/css/variables.css @@ -0,0 +1,105 @@ +:root { + /* Istang Color Palette */ + --color-burgundy: #8B2332; + --color-gold: #D4A574; + --color-forest: #2C5F2D; + --color-black: #000000; + + /* Tints and shades for depth */ + --color-burgundy-dark: #6B1A26; + --color-burgundy-light: #A63346; + --color-gold-dark: #B8925F; + --color-gold-light: #E6C399; + --color-forest-dark: #1F4520; + --color-forest-light: #3A7A3C; + + /* Neutral backgrounds */ + --color-bg-cream: #F5EFE7; + --color-bg-light: #FDFBF7; + --color-text-primary: #1A1A1A; + --color-text-secondary: #4A4A4A; + --color-white: #FFFFFF; + + /* Semantic colors */ + --color-primary: var(--color-burgundy); + --color-secondary: var(--color-gold); + --color-accent: var(--color-forest); + + /* Spacing scale (8px base) */ + --space-xs: 0.25rem; /* 4px */ + --space-sm: 0.5rem; /* 8px */ + --space-md: 1rem; /* 16px */ + --space-lg: 1.5rem; /* 24px */ + --space-xl: 2rem; /* 32px */ + --space-2xl: 3rem; /* 48px */ + --space-3xl: 4rem; /* 64px */ + --space-4xl: 6rem; /* 96px */ + + /* Border widths for istang patterns */ + --border-thin: 2px; + --border-medium: 4px; + --border-thick: 8px; + + /* Border radius */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + + /* Responsive breakpoints */ + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + + /* Layout */ + --container-max-width: 1200px; + --container-padding: var(--space-md); + --header-height: 100px; + --footer-height: 80px; + + /* Font families */ + --font-serif: Georgia, 'Times New Roman', serif; + --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + 'Helvetica Neue', Arial, sans-serif; + + /* Type scale */ + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.875rem; /* 30px */ + --text-4xl: 2.25rem; /* 36px */ + --text-5xl: 3rem; /* 48px */ + + /* Line heights */ + --leading-tight: 1.25; + --leading-normal: 1.5; + --leading-loose: 1.75; + + /* Font weights */ + --font-normal: 400; + --font-medium: 500; + --font-semibold: 600; + --font-bold: 700; + + /* Transitions */ + --transition-fast: 150ms ease; + --transition-base: 300ms ease; + --transition-slow: 500ms ease; + + /* Shadows */ + --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2); + + /* Z-index layers */ + --z-base: 1; + --z-dropdown: 100; + --z-sticky: 200; + --z-fixed: 300; + --z-modal: 400; + --z-popover: 500; + --z-tooltip: 600; +} diff --git a/src/assets/images/block-min.png b/src/assets/images/block-min.png new file mode 100644 index 0000000..717543d Binary files /dev/null and b/src/assets/images/block-min.png differ diff --git a/src/assets/images/block.png b/src/assets/images/block.png new file mode 100644 index 0000000..02f759f Binary files /dev/null and b/src/assets/images/block.png differ diff --git a/src/assets/images/patterns/corner-ornament.svg b/src/assets/images/patterns/corner-ornament.svg new file mode 100644 index 0000000..d784799 --- /dev/null +++ b/src/assets/images/patterns/corner-ornament.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/patterns/divider-pattern.svg b/src/assets/images/patterns/divider-pattern.svg new file mode 100644 index 0000000..d43ce90 --- /dev/null +++ b/src/assets/images/patterns/divider-pattern.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/patterns/spiral-border.svg b/src/assets/images/patterns/spiral-border.svg new file mode 100644 index 0000000..31f6bf6 --- /dev/null +++ b/src/assets/images/patterns/spiral-border.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/style.css b/src/assets/style.css index a8c73c4..28674c1 100644 --- a/src/assets/style.css +++ b/src/assets/style.css @@ -1,15 +1,23 @@ -body { - background-color: #f4f4f4; -} +/* Chechen Language Projects - Istang Design System */ -header { - margin-bottom: 20px; -} +/* CSS Custom Properties - Foundation */ +@import url('css/variables.css'); -.container { - margin-top: 20px; -} +/* Base Layer */ +@import url('css/reset.css'); +@import url('css/typography.css'); +@import url('css/layout.css'); -footer { - margin-top: auto; -} +/* Pattern System */ +@import url('css/patterns.css'); + +/* Components */ +@import url('css/components/header.css'); +@import url('css/components/footer.css'); +@import url('css/components/navigation.css'); +@import url('css/components/cards.css'); +@import url('css/components/buttons.css'); +@import url('css/components/forms.css'); + +/* Utilities - Last */ +@import url('css/utilities.css'); diff --git a/src/index.md b/src/index.md index 0ed4c51..851e8d7 100644 --- a/src/index.md +++ b/src/index.md @@ -1,16 +1,49 @@ --- title: Home layout: layout.njk +description: Discover open source Chechen language tools including Latin keyboard layouts for all platforms and advanced transliteration tools for Cyrillic to Latin script conversion. --- -# Welcome to the Chechen Language Projects +

Welcome to the Chechen Language Projects

-This is the home page for the Chechen Language organization. Below you will find an overview of our repositories. +

This is the home page for the Chechen Language organization. Below you will find an overview of our repositories.

-## Repositories +

Our Projects

-- [Chechen Latin Keyboard for macOS](/repositories/chechen-latin-keyboard-macos) -- [Chechen Latin Keyboard for Windows](/repositories/chechen-latin-keyboard-windows) -- [Chechen Latin Keyboard for Linux](/repositories/chechen-latin-keyboard-linux) -- [Chechen Latin Keyboard for iOS](/repositories/chechen-latin-keyboard-ios) -- [Chechen Latin Keyboard for Android](/repositories/chechen-latin-keyboard-android) +
+
+

Chechen Transliterator

+

Convert text between Cyrillic and Latin scripts with intelligent transliteration.

+ Learn more → +
+ +
+

macOS Keyboard

+

Chechen Latin keyboard layout for macOS systems.

+ Learn more → +
+ +
+

Windows Keyboard

+

Chechen Latin keyboard layout for Windows systems.

+ Learn more → +
+ +
+

Linux Keyboard

+

Chechen Latin keyboard layout for Linux systems.

+ Learn more → +
+ +
+

iOS Keyboard

+

Chechen Latin keyboard layout for iOS devices.

+ Learn more → +
+ +
+

Android Keyboard

+

Chechen Latin keyboard layout for Android devices.

+ Learn more → +
+
diff --git a/src/repositories/chechen-transliterator/index.md b/src/repositories/chechen-transliterator/index.md index bc2f20e..5d7fc33 100644 --- a/src/repositories/chechen-transliterator/index.md +++ b/src/repositories/chechen-transliterator/index.md @@ -1,23 +1,30 @@ --- layout: layout.njk title: Chechen Transliterator +description: Convert Chechen text between Cyrillic and Latin scripts with intelligent transliteration. Includes special handling for word-ending 'н' and automatic blacklist detection. --- -

Chechen Transliterator

-

Enter your text in the box below and click "Transliterate" to see the transliterated version. Special handling is applied to the letter 'н' at the end of words:

- - - -

Output:

+

Chechen Transliterator

+ +
+

Enter your text in the box below and click "Transliterate" to see the transliterated version. Special handling is applied to the letter 'н' at the end of words:

+
    +
  • If the word is in the blacklist, 'н' is transliterated as 'n'.
  • +
  • If the word is in the unsure list, 'н' is transliterated as 'ŋ[REPLACE]' to indicate manual review is needed.
  • +
  • Otherwise, 'н' at the end of a word is transliterated as 'ŋ'.
  • +
+ + + + + +

Output:

+
{% block extra_scripts %} diff --git a/src/robots.txt b/src/robots.txt new file mode 100644 index 0000000..7128694 --- /dev/null +++ b/src/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://chechen-language.github.io/sitemap.xml diff --git a/src/sitemap.xml b/src/sitemap.xml new file mode 100644 index 0000000..c88196b --- /dev/null +++ b/src/sitemap.xml @@ -0,0 +1,57 @@ + + + + https://chechen-language.github.io/ + 2024-01-04 + weekly + 1.0 + + + https://chechen-language.github.io/repositories/chechen-transliterator/ + 2024-01-04 + monthly + 0.9 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-macos/ + 2024-01-04 + monthly + 0.8 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-windows/ + 2024-01-04 + monthly + 0.8 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-linux/ + 2024-01-04 + monthly + 0.8 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-ios/ + 2024-01-04 + monthly + 0.8 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-android/ + 2024-01-04 + monthly + 0.8 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-android/privacy-policy-en/ + 2024-01-04 + yearly + 0.3 + + + https://chechen-language.github.io/repositories/chechen-latin-keyboard-android/privacy-policy-ru/ + 2024-01-04 + yearly + 0.3 + +