Damla Okul — Proje Mimarisi
Dokümantasyon: Bu projenin belgeleri dört dosyada toplanmıştır; ilgili değişiklikte o dosyayı güncelleyin:
- README.md — Genel bakış, kurulum ve hızlı başlangıç
- project.md — Teknik mimari ve geliştirme kuralları (bu dosya)
- design.md — Stil, tasarım sistemi ve UI bileşenleri
- getdata.md — Dış veri çekimi (TurkiyeAPI, MEB okullar / okul detay, nüfus)
Bu belge, damlaokul.com (Damla Okul) statik sitesinin teknik yapısını, dosya organizasyonunu ve geliştirme kurallarını açıklar. Stil için design.md; TurkiyeAPI ve MEB JSON’ları için getdata.md.
Genel Bakış
| Özellik | Değer |
|---|---|
| Tür | Statik site (Jamstack) |
| Motor | Jekyll 3.10 (github-pages gem v232) |
| Dil | Türkçe (language: tr) |
| Yayın | GitHub Pages (CNAME → damlaokul.com) |
| Build çıktısı | _site/ (gitignore) |
| Node/npm | Yok — CSS doğrudan düzenlenir, build adımı gerekmez |
Site, Damla Okul markası altında Damla Yayınevi’nin okul yayınlarını ve eğitim setlerini listeleyen bir ürün kataloğudur. İçerik Markdown + YAML front matter ile yönetilir; görünüm Bootstrap 5 ve özel CSS katmanlarıyla sağlanır.
Mimari Diyagram
┌─────────────────────────────────────────────────────────────┐
│ İçerik (Markdown + YAML) │
│ _books/ _catalogs/ _pages/ _posts/ _slides/ index │
└──────────────────────────┬──────────────────────────────────┘
│ Jekyll build
▼
┌─────────────────────────────────────────────────────────────┐
│ Şablonlar │
│ _layouts/ → default.html → book.html, page.html, … │
│ _includes/ → book-card, slider, instagram-carousel, search, footer │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Statik çıktı (_site/) │
│ HTML + assets/css + assets/js + assets/images │
└──────────────────────────┬──────────────────────────────────┘
│ git push
▼
GitHub Pages (damlaokul.com)
CSS Katmanları
Stil dosyaları ve sorumluluk ayrımı design.md dosyasında ayrıntılı olarak tanımlıdır.
Özet: theme.css (bileşen + token) → app.css (Bootstrap override). bootstrap.min.css düzenlenmez.
Klasör Yapısı
damlaegitim/
├── _config.yml # Site ayarları, koleksiyonlar, plugin’ler
├── README.md # Kurulum
├── project.md # Mimari (bu belge)
├── design.md # Tasarım sistemi
├── getdata.md # Dış veri çekimi (TurkiyeAPI, MEB)
├── _books/ # Ürünler (kitap / eğitim seti) — ~187 kayıt
├── _catalogs/ # PDF/flipbook kataloglar
├── _slides/ # Anasayfa slider verisi (output: false)
├── _posts/ # Blog yazıları
├── _pages/ # Statik sayfalar (hakkımızda, ürünler, iletişim…)
├── _layouts/ # HTML şablonları
├── _includes/ # Yeniden kullanılabilir parçalar
├── assets/
│ ├── css/
│ │ ├── bootstrap.min.css
│ │ ├── fontawesome-all.min.css
│ │ ├── theme.css
│ │ ├── app.css
│ │ ├── ogretmen-wizard.css # Öğretmen talep formu (/ogretmen)
│ │ ├── spotlight.css
│ │ ├── tiny-slider.css
│ │ └── buyout.css # Kullanılmıyor (legacy; layout’ta yüklenmez)
│ ├── fonts/
│ │ ├── fontawesome/ # Font Awesome 5.15.4 webfonts (tam paket)
│ │ ├── Koho/ # KoHo (TTF + WOFF2 subset; gövde + navbar)
│ │ ├── punta/
│ │ ├── geometric-sans/ # CSS'te referans yok; repo'da kalır
│ │ ├── raykjavik/ # CSS'te referans yok; repo'da kalır
│ │ └── monolight/ # CSS'te referans yok; repo'da kalır
│ ├── js/
│ │ ├── bootstrap.bundle.min.js
│ │ ├── nav.js # Navbar scroll (vanilla JS)
│ │ ├── book-filter.js # Sınıf/tür filtreleme (/ , /urunler, /ogretmen wizard)
│ │ ├── lunr.js # Arama (lazy-load; açılınca yüklenir)
│ │ ├── tiny-slider.js # Anasayfa slider
│ │ └── theme.js # Eski (jQuery) — kullanılmıyor
│ └── images/
│ ├── slides/ # Anasayfa slider görselleri (webp otomatik üretilebilir)
│ ├── ean/ # Kitap kapak görselleri (webp otomatik üretilebilir)
│ └── favicon/
├── _data/
│ ├── webp_manifest.yml # Otomatik: mevcut .webp listesi (generate_webp.sh)
│ ├── turkiye_adres_il_ilce.json # İl / ilçe (wizard; sync_site_data.py türetir)
│ ├── tymm.json # TYMM müfredat + Erdem-Değer çerçevesi (build_tymm_reference.rb)
│ ├── dersler.json # …
├── docs/ # Yerel kaynak (gitignore + Jekyll exclude)
│ └── data/ # Kanonik fetch JSON (getdata.md)
│ ├── turkiye_adres.json
│ ├── turkiye_geodata.json
│ ├── okullar.json
│ ├── okullar_detay.json
│ ├── tymm/ # TYMM ham API, PDF, referans CSV
│ └── reference/ # TurkiyeAPI / HDX vendor snapshot
├── assets/
│ └── data/
│ ├── okullar.json # Wizard fetch (sync; gitignore)
│ ├── geodata/ # Harita sınır parçaları (sync; gitignore)
│ └── okullar-harita/ # Okul listesi + detay il parçaları (sync; gitignore)
├── scripts/
│ ├── install_image_tools.sh # WebP/ImageMagick kurulumu (install.sh; winget/brew/apt)
│ ├── generate_webp.sh # jpg/png → .webp + manifest güncelleme (start.sh hook)
│ ├── sync_site_data.py # docs/data → site türetilmiş dosyalar (start.sh hook)
│ ├── refresh_image_paths.sh # Windows winget PATH düzeltmesi (dahili)
│ ├── check_images.sh # Büyük görsel uyarı raporu (start.sh hook)
│ ├── check_fonts.sh # Font / WOFF2 uyarı raporu (install.sh)
│ ├── subset_font.sh # Tüm OTF/TTF → WOFF2 subset (install.sh)
│ ├── normalize_book_frontmatter.rb # Kitap front matter sıralama/normalize (`preview_link`, `examlink`, `damlaurl`; eski alan migrasyonu)
│ ├── map_story_metadata.rb # Story kitap: anatema, degerler, egilimler, beceriler, unite (orchestrator)
│ ├── curriculum_lib.rb # TYMM/anatema sözlük yardımcıları (map_story_*)
│ ├── ogretmen-submit.gs # Öğretmen wizard → Sheets + mail (Workspace’te dağıtılır; repo referans kopyası)
│ ├── fetch_turkiyeadres.py # → docs/data/turkiye_adres.json (getdata.md)
│ ├── fetch_okullar.py # → docs/data/okullar.json kamu (getdata.md)
│ ├── fetch_ozel_okullar.py # → okullar.json özel birleştirme (getdata.md)
│ ├── fetch_okuldetay.py # → docs/data/okullar_detay.json (getdata.md)
│ ├── fetch_population.py # → docs/data/population.json (getdata.md)
│ └── sync_site_data.py # → site türetilmiş dosyalar (getdata.md)
├── index.html # Anasayfa
├── Gemfile # github-pages + webrick (canlı GitHub Pages ile aynı stack)
├── CNAME # damlaokul.com
├── install.sh # İlk kurulum: bundle, fonttools, WOFF2, görsel araçları, jekyll build
├── start.sh # Geliştirme: check_images + generate_webp + sync_site_data hook + jekyll serve
└── _site/ # Build çıktısı (gitignore)
Koleksiyonlar ve URL’ler
| Koleksiyon | Klasör | URL | Layout | Çıktı |
|---|---|---|---|---|
books |
_books/ |
/urunler/:title |
book |
Evet |
catalogs |
_catalogs/ |
/kataloglar/:title |
catalog |
Evet |
slides |
_slides/ |
— | — | Hayır (veri kaynağı) |
| Sayfalar | _pages/ |
/:title/ |
page |
Evet |
| Yazılar | _posts/ |
varsayılan | post |
Evet |
_pages klasörü _config.yml içinde include: ["_pages"] ile Jekyll kaynaklarına dahil edilir. docs/ klasörü gitignore ve _config.yml exclude listesindedir; Jekyll build çıktısına girmez.
Layout’lar
| Dosya | Kullanım |
|---|---|
default.html |
Kök şablon: head, navbar, footer, script’ler |
page.html |
Basit içerik sayfası |
page-sidebar.html |
İçerik + yan menü |
book.html |
Ürün detay sayfası |
previewbook.html |
Tam ekran önizleme |
catalog.html |
Katalog detay + iframe görüntüleyici |
post.html |
Blog yazısı |
person.html / illustrator.html / translator.html |
Kişi profilleri |
ogretmen-wizard.html |
6 adımlı öğretmen talep formu (/ogretmen) |
Tüm layout’lar layout: default zinciri üzerinden default.html’i extend eder.
Öğretmen talep formu (wizard)
URL: /ogretmen — Durum: Canlı (Jekyll → Apps Script → Sheets + e-posta)
Öğretmenler sınıf seçer, hikaye ve eğitim kitaplarından liste oluşturur, iletişim bilgilerini girer ve talebi gönderir. Statik sitede backend yok; gönderim Google Apps Script web uygulamasına fetch POST ile yapılır.
flowchart LR
Browser["Tarayıcı /ogretmen"]
GAS["Apps Script doPost"]
Sheet["Sheets Talepler"]
Mail["MailApp"]
Captcha["reCAPTCHA siteverify"]
Browser -->|"POST JSON"| GAS
GAS --> Captcha
GAS --> Sheet
GAS --> Mail
Sihirbaz adımları
| # | Adım | İçerik |
|---|---|---|
| 1 | Sınıf | Okul öncesi – 8. sınıf |
| 2 | Okuma listesi | Hikaye; anatema → değer → eğilim → beceri filtreleri (kademeli) |
| 3 | Eğitim kitapları | Eğitim setleri; arama |
| 4 | Liste | Seçilen ürünler; okuma + eğitim grupları |
| 5 | İletişim | Ad, soyad, il, ilçe, telefon, e-posta, okul |
| 6 | Gönder | Özet, reCAPTCHA v2, gönder |
State: localStorage → damlaokul:ogretmen-wizard (version: 5). Sınıf değişince liste ve filtreler sıfırlanır. Katalog build’de site.books JSON; listeye eklenen kitap katalogdan çıkar.
Dosyalar
| Dosya | Görev |
|---|---|
_pages/ogretmen.html |
Permalink, SEO, footer |
_layouts/ogretmen-wizard.html |
Adımlar, state, katalog JSON, submit |
_includes/ogretmen-wizard/step-*.html |
6 adım UI |
assets/css/ogretmen-wizard.css |
Wizard stilleri (design.md) |
assets/js/book-filter.js |
TYMM filtreleri |
_data/turkiye_adres_il_ilce.json |
İl / ilçe wizard embed; sync_site_data.py türetir — getdata.md |
docs/data/okullar.json |
MEB kamu + OOKGM özel kurum listesi (kanonik) — getdata.md |
assets/data/okullar.json |
Wizard fetch; sync ile üretilir, git’te yok — getdata.md |
docs/data/okullar_detay.json |
Okul meta monolit (kurum_kodu); sync ile assets/data/okullar-harita/ il parçalarına bölünür — getdata.md |
docs/data/population.json |
İl/ilçe nüfus ve çocuk sayıları (TurkiyeAPI + TÜİK ADNKS vendor); site sync yok — getdata.md |
assets/data/okullar-harita/ |
Harita sayfası lazy fetch; sync türetilmiş — getdata.md |
_data/tymm.json |
Hikâye filtre sıralaması; Erdem-Değer çerçevesi (D1–D20); öğretmen sihirbazı değer listesi |
scripts/ogretmen-submit.gs |
Backend referansı (Workspace’te dağıtılır) |
Yapılandırma (_config.yml)
ogretmen_submit_url: "https://script.google.com/macros/s/…/exec"
ogretmen_recaptcha_site_key: "…" # v2 site key (herkese açık)
Gizli anahtar config’e yazılmaz — Apps Script Script Properties → RECAPTCHA_SECRET.
Gönderim akışı
buildSheetRow()→ JSON (talep_id, iletişim, ürünler,filtre_*,recaptcha_token,urunler[])POST,Content-Type: text/plain;charset=utf-8- Yanıt
{ ok: true }→ başarı ekranı;localStoragetemizlenir - Hata → öğretmene genel mesaj; ayrıntı
console.log('Gönderim hatası:', …)
Google Workspace kurulumu
- Shared Drive’da Öğretmen Talepleri e-tablosu; Apps Script e-tabloya bağlı
scripts/ogretmen-submit.gsiçeriğini editöre yapıştır- Script properties:
NOTIFY_EMAIL(virgülle alıcılar),RECAPTCHA_SECRET(gizli anahtar) izinleriAlfonksiyonunu editörden çalıştır → UrlFetchApp izni ver- Web app deploy: Execute as Me, Anyone; kod değişince New version
/execURL →ogretmen_submit_url; siteyi yeniden deploy et
Sheet sayfaları (ilk POST’ta oluşur): Talepler, Talep_Urunleri. Form filtre_anatema / filtre_degerler / filtre_egilimler / filtre_beceriler de gönderir; Sheet başlıklarına isteğe bağlı eklenir.
Tarayıcıda /exec URL’sini GET ile açmak doGet not found döner — normal (doPost only).
Mail hatası Sheet kaydını iptal etmez.
Sorun giderme
| Console / belirti | Çözüm |
|---|---|
UrlFetchApp… izniniz yok |
izinleriAl + izin ver + yeni sürüm deploy |
invalid-input-secret |
RECAPTCHA_SECRET = gizli anahtar (site key değil) |
missing_token |
reCAPTCHA yeniden işaretle |
| Sheet var, mail yok | NOTIFY_EMAIL / MailApp kotası |
| Eski davranış | Deploy → New version |
Yerel test: sh start.sh → http://localhost:4000/ogretmen (reCAPTCHA admin’de localhost tanımlı olmalı).
Önemli Include’lar
| Dosya | Görev |
|---|---|
menu-header.html |
Ana navigasyon linkleri |
footer-menu.html |
3 sütunlu site footer (ürünler, önemli bilgiler, iletişim) |
contact-info.html |
Site geneli iletişim bilgileri (site.contact_*, footer ve iletişim sayfası) |
menu-socialmedia.html |
Sosyal medya ikonları |
slider.html |
Anasayfa Tiny Slider (<picture>, WebP, lazyload, fetchpriority) |
instagram-carousel.html |
Anasayfa Instagram carousel (okul.damla, Behold JSON feed) |
book-grade-nav.html |
Sınıf sekmesi + tür alt menüsü |
book-home-groups.html |
Anasayfa kitap listesi (Eğitim / Hikaye) |
book-card.html |
Tek kitap kartı partial (<picture> + WebP, native lazy loading) |
book-grade-filter.html |
Eski sınıf checkbox dropdown’u (artık kullanılmıyor) |
search-lunr.html |
Spotlight arama (lazy-load Lunr + JSON indeks, modal UI, navbar tetikleyici) |
popup.html |
Kitap detay popup’ları (Satın Al / Bilgi, İncele iframe, YouTube, HDS; koyu tema desteği) |
eanimage.html |
Kitap kapak <picture> + koşullu WebP (eanimage partial) |
tracking-header.html / tracking-footer.html |
Google Analytics |
JavaScript
| Dosya | Yükleme | Görev |
|---|---|---|
bootstrap.bundle.min.js |
defer, tüm sayfalar |
Collapse, dropdown, modal |
nav.js |
defer, tüm sayfalar |
Sticky navbar yüksekliği (--nav-height), scroll gölgesi |
book-filter.js |
defer, /, /urunler, /ogretmen |
Sınıf/tür filtreleme + hash URL; wizard’da TYMM filtreleri |
lunr.js |
Lazy — arama açılınca | Spotlight kitap araması (client-side indeks) |
tiny-slider.js |
Anasayfa (slider.html) |
Slider + lazyload |
jQuery kullanılmaz. lazyload.js kaldırıldı; görseller native loading="lazy" ve <picture> ile yönetilir.
instagram-carousel.html kendi inline <script> bloğunu taşır; feed IntersectionObserver ile viewport’a girince yüklenir.
search-lunr.html navbar Spotlight aramasını sağlar. mode parametresiyle navbar tetikleyicisi (desktop / mobile) veya script bloğu (mode olmadan) ayrı include edilir.
- Masaüstü (
mode='desktop') — Navbar collapse içinde tam arama kutusu (Ctrl+Kkısayolu) - Mobil (
mode='mobile') — Logo ile hamburger arasında minimal arama çubuğu (Kitap ara...); collapse dışında, her zaman görünür
Performans Optimizasyonu
Site hızı için uygulanan başlıca önlemler:
LCP (Hero Slider)
- Mobil-öncelik
<picture>: mobil webp/jpeg önce, desktopmin-width: 768px - İlk slide
<img src>mobil jpeg;fetchpriority="high",width/heightipucu - Preload: mobil webp (
max-width: 767px); desktop webp veya jpeg (min-width: 768px) — anasayfa head tiny-slider.js+slider-init.jsdefer; inline init kaldırıldı- Diğer slide’lar: Tiny Slider
lazyload: true+loading="lazy" tiny-slider.cssanasayfa head’de preload + link (body içi link yok)
Görseller
- Kitap kartları:
<picture>+ koşullu WebP,loading="lazy",fetchpriority="low" - jpg/png Photoshop ile optimize edilir (resize/sıkıştırma); ImageMagick resize kullanılmaz
.webpscripts/generate_webp.shile otomatik üretilir (start.shhook)_data/webp_manifest.yml— mevcut webp listesi; şablonlarda koşullu<source type="image/webp">install.sh→install_image_tools.sh: Windows’ta winget ile ImageMagick + libwebp kurulumuscripts/refresh_image_paths.sh— winget kurulum yollarını Git Bash PATH’ine ekler- Araç yoksa (
cwebp/ ImageMagick) site jpg ile çalışır, 404 oluşmaz scripts/check_images.shbüyük dosyaları raporlar (dosyaya dokunmaz; ImageMagickidentifykullanır)start.shher geliştirme oturumunda görsel kontrolü ve webp üretimini denemek
JavaScript (TBT / TTI)
- Lunr araması lazy-load:
lunr.jsve indeks yalnızca arama açılınca yüklenir - Arama indeksi:
_pages/search-index.json→/assets/search-index.json(build-time JSON) book-filter.jsyalnızca anasayfa ve/urunler; anasayfa initrequestIdleCallback(fallbacksetTimeout)- Bootstrap
deferile yüklenir
Fontlar ve CSS
- Font Awesome yerel (
assets/fonts/fontawesome/), CDN kaldırıldı - KoHo WOFF2 subset (~14 KB / ağırlık; Regular, Medium, Bold, Italic); TTF repo’da kalır, servis edilmez
- KoHo Regular font preload (
default.html) spotlight.cssasync yükleme (media="print" onload).book-genre-group:content-visibility: auto(below-fold paint maliyeti)
Cloudflare mobil benchmark (referans)
Skor ~55; LCP ~11.5 s, TTI ~13.3 s (büyük mobil jpeg, senkron slider JS). Kod düzeltmeleri sonrası Cloudflare Speed Test ile mobil retest önerilir.
Instagram Carousel
- Behold feed isteği
IntersectionObserverile carousel viewport’a girince başlar
Cloudflare
- Şu an DNS-only (gri bulut); proxy veya cache ayarı bu repoda dokümante edilmez.
Yerel Araçlar
# İlk kurulum (git clone sonrası)
sh install.sh # bundle, fonttools, WOFF2, görsel araçları, jekyll build
# Geliştirme (hook'lar yalnızca burada)
sh start.sh # check_images + generate_webp + jekyll serve
# Manuel script'ler
sh scripts/install_image_tools.sh # WebP/ImageMagick (Windows: winget)
sh scripts/generate_webp.sh # .webp üretimi + webp_manifest.yml
sh scripts/check_images.sh # Büyük görsel uyarı raporu
sh scripts/check_images.sh slides # Yalnızca slider
sh scripts/check_images.sh ean # Yalnızca kitap kapakları
sh scripts/check_fonts.sh # Font / WOFF2 uyarı raporu
sh scripts/subset_font.sh # Tüm OTF/TTF → WOFF2 subset (fontawesome hariç)
install.sh vs start.sh
install.sh |
start.sh |
|
|---|---|---|
| Amaç | Sıfırdan kurulum | Günlük geliştirme |
| Ruby / bundle | bundle install |
Gemfile.lock kontrolü |
| Fontlar | subset_font.sh, check_fonts.sh |
— |
| Görsel araçları | install_image_tools.sh (winget/brew/apt) |
— |
| Görsel hook’ları | — | check_images.sh, generate_webp.sh, sync_site_data.py |
| Jekyll | jekyll build (doğrulama) |
jekyll serve |
_data/webp_manifest.yml |
Yoksa oluşturur | generate_webp.sh günceller |
Navbar (#MagicMenu)
Ana navigasyon default.html içinde id="MagicMenu" ile tanımlıdır; scroll sırasında her zaman üstte sabit kalır.
Yapı, responsive davranış ve görsel stiller design.md — Site Navbar bölümünde tanımlıdır.
nav.js
--nav-heightCSS değişkenini navbar yüksekliğine göre günceller (ResizeObserverile mobil menü açılınca da)- Scroll’da
site-nav--scrolledsınıfı ile hafif gölge ekler
Kitap Detay Sayfası (book.html)
Ürün detay layout’u book-page sınıfı ile işaretlenir. Etiket stilleri, prose düzeni ve popup görünümü design.md — Kitap Detay bölümünde tanımlıdır.
Üst alan (hero meta)
Hikaye kitaplarında (genre: story) müfredat bilgisi başlık altında book-hero-meta.html içindeki .book-curriculum paneli ile gösterilir. Panel iki bölümden oluşur:
Türkiye Yüzyılı Maarif Modeli
| UI etiketi | Front matter | Görünüm |
|---|---|---|
| Anatemalar: | anatema |
Outline chip; yatay scroll şeridi |
| Erdemler / Değerler: | degerler |
Outline yeşil chip; yatay scroll şeridi |
| Eğilimler: | egilimler |
Outline chip; yatay scroll şeridi |
| Beceriler: | beceriler |
Düz chip listesi; yatay scroll şeridi |
Türkiye’de İlk ve Tek Damla Hikaye Kazanım Sistemi
| UI etiketi | Front matter | Görünüm |
|---|---|---|
| Öykümatik Kazanım Kodu: | kazanim |
Kod chip’leri; title ile kazanım metni; yatay scroll |
Etiketler (panel altında)
| UI etiketi | Front matter | Görünüm |
|---|---|---|
| (etiket) | tags |
Turuncu pill |
Mobilde müfredat satırları dikey (etiket üstte, chip şeridi altta tam genişlik); sağ kenarda fade ve chevron kaydırma ipucu verir.
Görünür metinde # / @ öneki kullanılmaz. Müfredat ve etiket alanlarının tamamı boşsa hero meta bloğu render edilmez.
Eğitim setlerinde (genre: education) müfredat kanıtı gövdedeki Maarif uyum tablosu (markdown tablo, .maarif-content stili) ile sunulur; ayrıntı maarif-modeli.
Filtre bloğu sırası (# Spesific Filterable Attributes)
genre → grades → tags → degerler → anatema → egilimler → kazanim → beceriler → unite
tags üst bölümde (categories yanında) tutulmaz; normalize_book_frontmatter.rb aynı sırayı yazar.
Eski üst alan notu (kaldırıldı)
Önceden anatemalar ve kavramlar yan kolondaki «Öğretmen için» kutusundaydı; TYMM revizyonuyla degerler, anatema, egilimler, beceriler ve kazanim hero başlık altında gösterilir (unite story kitaplarda UI/filtre dışı).
Aksiyon butonları ve popup
Tanıtım (YouTube), Satın Al / Bilgi, İncele, HDS ve Akıllı Tahta İndir butonları .js-book-action ile popup.html üzerinden açılır (book-detail-actions.html):
| Buton | Koşul | Davranış |
|---|---|---|
| Tanıtım | youtube dolu |
YouTube embed / yeni sekme |
| Satın Al | damlaurl dolu |
iframe popup (ürün sayfası) |
| Bilgi | damlaurl boş |
Tedarik bilgisi modal |
| İncele | preview_link dolu |
iframe popup (ön izleme) |
| HDS | examlink dolu |
iframe popup (PDF) |
| Akıllı Tahta İndir | genre: education + akillitahta en az bir URL dolu |
Platform seçim popup (indirme) |
data-popup-type |
Davranış |
|---|---|
info |
Tedarik bilgisi modal (mobil + masaüstü) |
iframe |
Tam ekran iframe (Satın Al, İncele, HDS) |
youtube |
YouTube embed |
akillitahta |
Platform seçimi: exe → Windows (.exe), deb → Pardus (.deb), appimage → Pardus (.AppImage), dmg → macOS (.dmg) |
Masaüstünde iframe/youtube popup; mobilde yeni sekme. info ve akillitahta her zaman popup. info popup’u prefers-color-scheme: dark destekler.
Spotlight Arama
Navbar üzerinden kitap araması yapılır. macOS Spotlight benzeri tam ekran modal açılır.
Dosyalar
| Dosya | Görev |
|---|---|
_includes/search-lunr.html |
Lazy-load arama scripti, navbar tetikleyici |
_pages/search-index.json |
Build-time arama indeksi (/assets/search-index.json) |
assets/js/lunr.js |
Lunr.js 2.1.5 kütüphanesi (arama açılınca yüklenir) |
assets/css/spotlight.css |
Modal stilleri (async yükleme) |
İndeks kapsamı
site.books koleksiyonu; alanlar: title, ean, authors, categories, grades, genre, degerler, anatema, egilimler, kazanim, beceriler, tags, body. Kapak görseli indekste assets/images/ean/{ean}.webp (yoksa .jpg) kullanılır.
4+ haneli tamamen sayısal aramalarda ean alanında doğrudan eşleştirme yapılır; diğer sorgularda Lunr wildcard araması kullanılır.
İndeks her sayfaya gömülmez; /assets/search-index.json olarak ayrı endpoint’ten fetch edilir. Bu sayede sayfa yüklenirken Lunr indeksleme maliyeti oluşmaz.
Kullanım
- Navbar’daki arama kutusuna tıklama (masaüstü tam kutu; mobil logo–hamburger arası minimal çubuk)
Ctrl+K/⌘Kklavye kısayolu- Enter → ilk sonuca git; ESC → kapat
Eski theme.js içindeki loadSearch() (content.json tabanlı) kullanılmaz.
Instagram Carousel
Anasayfada @okul.damla hesabının güncel gönderilerini yatay carousel olarak gösterir. Modül tek dosyada inline HTML, CSS ve vanilla JS içerir.
Yapılandırma (_config.yml)
instagram_carousel:
username: okul.damla
profile_url: https://instagram.com/okul.damla
feed_url: '' # Behold JSON feed URL
limit: 12
feed_url boşsa modül build’i kırmaz; kullanıcıya yapılandırma mesajı ve Instagram profil linki gösterilir.
Veri kaynağı
Instagram, statik siteden doğrudan feed çekmeye izin vermez. Behold.so JSON feed kullanılır:
- Behold hesabında
okul.damlakaynak olarak bağlanır - Çıktı tipi JSON olan feed oluşturulur
https://feeds.behold.so/XXXXURL’sifeed_urlalanına yazılır
Kullanım
<section class="ig-carousel"
id="ig-carousel-okul-damla"
data-feed-url="https://feeds.behold.so/k4ylFSaGtUBY15ZsyXAt"
data-username="okul.damla"
data-profile-url="https://instagram.com/okul.damla"
data-limit="12"
aria-label="Instagram gönderileri">
<div class="container">
<header class="ig-carousel__header">
<h3 class="ig-carousel__heading">
<i class="fab fa-instagram" aria-hidden="true"></i>
<span>@okul.damla</span>
</h3>
<a class="ig-carousel__follow"
href="https://instagram.com/okul.damla"
target="_blank"
rel="noopener noreferrer">
Takip Et
</a>
</header>
<div class="ig-carousel__viewport">
<button class="ig-carousel__nav ig-carousel__nav--prev"
type="button"
aria-label="Önceki gönderiler"
hidden>
<i class="fas fa-chevron-left" aria-hidden="true"></i>
</button>
<div class="ig-carousel__track" role="list" aria-live="polite">
<div class="ig-carousel__item ig-carousel__item--skeleton" role="listitem" aria-hidden="true">
<div class="ig-carousel__skeleton"></div>
</div>
<div class="ig-carousel__item ig-carousel__item--skeleton" role="listitem" aria-hidden="true">
<div class="ig-carousel__skeleton"></div>
</div>
<div class="ig-carousel__item ig-carousel__item--skeleton" role="listitem" aria-hidden="true">
<div class="ig-carousel__skeleton"></div>
</div>
<div class="ig-carousel__item ig-carousel__item--skeleton" role="listitem" aria-hidden="true">
<div class="ig-carousel__skeleton"></div>
</div>
</div>
<button class="ig-carousel__nav ig-carousel__nav--next"
type="button"
aria-label="Sonraki gönderiler"
hidden>
<i class="fas fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
<p class="ig-carousel__status" hidden></p>
</div>
</section>
<style>
.ig-carousel {
margin: 2rem 0;
}
.ig-carousel__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1rem;
}
.ig-carousel__heading {
font-family: var(--font-display, "Punta", sans-serif);
font-size: 1.1rem;
font-weight: 700;
color: #343a40;
margin: 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e9ecef;
flex: 1;
display: flex;
align-items: center;
gap: 0.5rem;
}
.ig-carousel__heading .fab {
color: var(--color-primary, #03a87c);
font-size: 1.25rem;
}
.ig-carousel__follow {
flex-shrink: 0;
display: inline-flex;
align-items: center;
padding: 0.4rem 1rem;
font-size: 0.875rem;
font-weight: 600;
color: #fff;
background: var(--color-primary, #03a87c);
border-radius: var(--radius, 0.5rem);
text-decoration: none;
transition: background 0.2s ease;
}
.ig-carousel__follow:hover {
background: var(--color-primary-dark, #028a66);
color: #fff;
text-decoration: none;
}
.ig-carousel__viewport {
position: relative;
}
.ig-carousel__track {
display: flex;
gap: 0.75rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
padding: 0.25rem 0;
}
.ig-carousel__track::-webkit-scrollbar {
display: none;
}
.ig-carousel__item {
flex: 0 0 calc(50% - 0.375rem);
scroll-snap-align: start;
min-width: 0;
}
@media (min-width: 576px) {
.ig-carousel__item {
flex: 0 0 calc(33.333% - 0.5rem);
}
}
@media (min-width: 992px) {
.ig-carousel__item {
flex: 0 0 calc(25% - 0.5625rem);
}
}
.ig-carousel__card {
display: block;
position: relative;
background: #fff;
border: 1px solid var(--color-border, #dee2e6);
border-radius: var(--radius, 0.5rem);
overflow: hidden;
aspect-ratio: 1;
text-decoration: none;
transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.ig-carousel__card:hover {
box-shadow: var(--shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.08));
transform: translateY(-2px);
text-decoration: none;
}
.ig-carousel__card img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.ig-carousel__caption {
position: absolute;
inset: 0;
display: flex;
align-items: flex-end;
padding: 0.75rem;
background: linear-gradient(transparent 40%, rgba(0, 0, 0, 0.65));
color: #fff;
font-size: 0.75rem;
line-height: 1.4;
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
}
.ig-carousel__card:hover .ig-carousel__caption,
.ig-carousel__card:focus-visible .ig-carousel__caption {
opacity: 1;
}
.ig-carousel__badge {
position: absolute;
top: 0.5rem;
right: 0.5rem;
width: 1.75rem;
height: 1.75rem;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.55);
color: #fff;
border-radius: 50%;
font-size: 0.7rem;
pointer-events: none;
}
.ig-carousel__skeleton {
width: 100%;
aspect-ratio: 1;
border-radius: var(--radius, 0.5rem);
background: linear-gradient(90deg, #e9ecef 25%, #f8f9fa 50%, #e9ecef 75%);
background-size: 200% 100%;
animation: ig-carousel-shimmer 1.4s ease-in-out infinite;
}
@keyframes ig-carousel-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.ig-carousel__nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 2;
width: 2.25rem;
height: 2.25rem;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--color-border, #dee2e6);
border-radius: 50%;
background: #fff;
color: var(--color-primary, #03a87c);
cursor: pointer;
box-shadow: var(--shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.08));
transition: background 0.2s ease, color 0.2s ease;
}
.ig-carousel__nav:hover {
background: var(--color-primary, #03a87c);
color: #fff;
}
.ig-carousel__nav--prev { left: -0.75rem; }
.ig-carousel__nav--next { right: -0.75rem; }
@media (max-width: 575px) {
.ig-carousel__nav { display: none; }
}
.ig-carousel__status {
margin: 0.75rem 0 0;
font-size: 0.875rem;
color: var(--color-muted, #6c757d);
text-align: center;
}
.ig-carousel__status a {
color: var(--color-primary, #03a87c);
font-weight: 600;
}
.ig-carousel--error .ig-carousel__track {
justify-content: center;
min-height: 8rem;
align-items: center;
}
</style>
<script>
(function () {
function escapeHtml(str) {
if (!str) return '';
var div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function getSizeUrl(size) {
if (!size) return '';
if (typeof size === 'string') return size;
return size.mediaUrl || size.url || '';
}
function getImageUrl(post) {
if (post.sizes) {
return getSizeUrl(post.sizes.medium) ||
getSizeUrl(post.sizes.small) ||
getSizeUrl(post.sizes.large) ||
getSizeUrl(post.sizes.full) ||
post.thumbnailUrl ||
'';
}
return post.thumbnailUrl || post.mediaUrl || '';
}
function getBadgeIcon(mediaType) {
if (mediaType === 'VIDEO') return 'fa-play';
if (mediaType === 'CAROUSEL_ALBUM') return 'fa-images';
return '';
}
function truncate(text, max) {
if (!text) return '';
return text.length > max ? text.slice(0, max).trim() + '…' : text;
}
function initCarousel(section) {
var feedUrl = (section.dataset.feedUrl || '').trim();
var profileUrl = section.dataset.profileUrl || 'https://instagram.com/' + section.dataset.username;
var limit = parseInt(section.dataset.limit, 10) || 12;
var track = section.querySelector('.ig-carousel__track');
var status = section.querySelector('.ig-carousel__status');
var prevBtn = section.querySelector('.ig-carousel__nav--prev');
var nextBtn = section.querySelector('.ig-carousel__nav--next');
function showStatus(message) {
status.innerHTML = message;
status.hidden = false;
}
function updateNav() {
if (!track || track.scrollWidth <= track.clientWidth + 1) {
prevBtn.hidden = true;
nextBtn.hidden = true;
return;
}
prevBtn.hidden = track.scrollLeft <= 1;
nextBtn.hidden = track.scrollLeft >= track.scrollWidth - track.clientWidth - 1;
}
function scrollByPage(direction) {
var item = track.querySelector('.ig-carousel__item');
var gap = 12;
var amount = item ? item.offsetWidth + gap : track.clientWidth * 0.8;
track.scrollBy({ left: direction * amount, behavior: 'smooth' });
}
prevBtn.addEventListener('click', function () { scrollByPage(-1); });
nextBtn.addEventListener('click', function () { scrollByPage(1); });
track.addEventListener('scroll', updateNav, { passive: true });
window.addEventListener('resize', updateNav);
if (!feedUrl) {
section.classList.add('ig-carousel--error');
track.innerHTML = '';
showStatus('Instagram akışı henüz yapılandırılmadı. <a href="' + escapeHtml(profileUrl) + '" target="_blank" rel="noopener noreferrer">@' + escapeHtml(section.dataset.username) + '</a> hesabını ziyaret edebilirsiniz.');
return;
}
fetch(feedUrl)
.then(function (res) {
if (!res.ok) throw new Error('Feed yüklenemedi');
return res.json();
})
.then(function (data) {
var posts = (data.posts || []).slice(0, limit);
if (!posts.length) throw new Error('Gönderi bulunamadı');
track.innerHTML = posts.map(function (post) {
var imgUrl = getImageUrl(post);
var permalink = post.permalink || profileUrl;
var caption = truncate(post.prunedCaption || post.caption || '', 100);
var badge = getBadgeIcon(post.mediaType);
var badgeHtml = badge
? '<span class="ig-carousel__badge"><i class="fas ' + badge + '" aria-hidden="true"></i></span>'
: '';
var captionHtml = caption
? '<span class="ig-carousel__caption">' + escapeHtml(caption) + '</span>'
: '';
return '<div class="ig-carousel__item" role="listitem">' +
'<a class="ig-carousel__card" href="' + escapeHtml(permalink) + '" target="_blank" rel="noopener noreferrer" aria-label="Instagram gönderisi">' +
'<img src="' + escapeHtml(imgUrl) + '" alt="' + escapeHtml(caption || 'Instagram gönderisi') + '" loading="lazy">' +
badgeHtml + captionHtml +
'</a></div>';
}).join('');
prevBtn.hidden = false;
nextBtn.hidden = false;
updateNav();
})
.catch(function () {
section.classList.add('ig-carousel--error');
track.innerHTML = '';
showStatus('Gönderiler yüklenemedi. <a href="' + escapeHtml(profileUrl) + '" target="_blank" rel="noopener noreferrer">@' + escapeHtml(section.dataset.username) + '</a> hesabını Instagram\'da ziyaret edin.');
});
}
function boot() {
document.querySelectorAll('.ig-carousel').forEach(function (section) {
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function (entries, obs) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
initCarousel(entry.target);
obs.unobserve(entry.target);
});
}, { rootMargin: '200px 0px' });
observer.observe(section);
} else {
initCarousel(section);
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();
</script>
Şu an index.html içinde slider ile kitap listesi arasında include edilir. Görsel tasarım design.md — Instagram Carousel bölümünde tanımlıdır.
Footer
Site alt bilgisi _includes/footer-menu.html ile 3 sütunlu olarak render edilir. default.html layout’u bu partial’ı otomatik include eder.
| Sütun | İçerik | Kaynak |
|---|---|---|
| Ürünler | Eğitim Kitapları, Hikaye Kitapları, Kataloglar | Sabit /urunler linkleri + site.pages (permalink: /kataloglar) |
| Önemli Bilgiler | Hakkımızda, Gizlilik vb. | _pages front matter: footer_show: true, footer_order |
| Adres & İletişim | Telefon, e-posta, adres, sosyal | _config.yml contact_* + menu-socialmedia.html |
Site geneli iletişim (_config.yml)
İletişim bilgileri kök seviyede tanımlanır (footer bloğu değil; sosyal ayarlar gibi site ayarı):
contact_phone: "+90 212 514 28 28"
contact_phone_href: "tel:+902125142828"
contact_email: "[email protected]"
contact_address: "Alemdar Mh. Prof. Kazım İsmail Gürkan Cad. No:8 Fatih, 34110 İstanbul"
Jekyll’da erişim: +90 212 514 28 28, [email protected] vb.
contact-info.html partial
Tek kaynak; footer ve iletişim sayfası buradan beslenir:
<div class="contact-info contact-info--footer">
<p class="contact-info__item">
<strong>Telefon:</strong>
<a href="tel:+902125142828">+90 212 514 28 28</a>
</p>
<p class="contact-info__item">
<strong>E-posta:</strong>
<a href="mailto:[email protected]">[email protected]</a>
</p>
<p class="contact-info__item">
<strong>Adres:</strong>
<span>Alemdar Mh. Prof. Kazım İsmail Gürkan Cad. No:8 Fatih, 34110 İstanbul</span>
</p>
</div>
{# footer sütunu #}
<div class="contact-info contact-info--page">
<h5>Adres</h5>
<p class="contact-info__item">Alemdar Mh. Prof. Kazım İsmail Gürkan Cad. No:8 Fatih, 34110 İstanbul</p>
<h5>Telefon</h5>
<p class="contact-info__item">
<a href="tel:+902125142828">+90 212 514 28 28</a>
</p>
<h5>E-posta</h5>
<p class="contact-info__item">
<a href="mailto:[email protected]">[email protected]</a>
</p>
</div>
{# iletişim sayfası gövdesi #}
_pages/iletisim.md harita iframe’ini sayfa gövdesinde tutar; telefon/e-posta/adres tekrarı partial üzerinden gelir.
Footer’da gösterilecek sayfalar
_pages front matter:
footer_show: true
footer_order: 10
Footer düzeni ve stiller design.md — Site Footer bölümünde tanımlıdır.
Ürün (Kitap) Front Matter Örneği
---
layout: book
title: "Deyim Öyküleri 5 Kitap"
grades: [3]
genre: story # education | story
tags: []
anatema: [Saygı, Empati]
kazanim: [H.1.2.1, H.3.3.3]
beceriler: [Okuma Becerisi, "Problem Çözme Becerisi"]
unite: ["Oyun Dünyası"]
categories: ["Çocuk", "Hikaye"]
ean: 9786053832874
preview_link: "https://cdn.e-damla.com.tr/PUBLIC/ornek-sayfalar/9786053832874/index.html"
examlink: "" # HDS yoksa boş; doluysa tam PDF URL (ör. https://cdn.e-damla.com.tr/PUBLIC/hds_pdf/y/...)
damlaurl: "" # Damla Yayınevi ürün sayfası; doluysa Satın Al, boşsa Bilgi → tedarik popup
languages: ["Türkçe"]
page: Her Biri 64
size: "14x20cm"
publish-number: 1443
cover: "Karton Kapak"
---
Markdown gövdesi ürün açıklaması olarak book.html içindeki .prose--display alanında render edilir. **TEMALAR:** gibi kalın başlıklar ve madde listeleri bu alanda stillenir.
Ön izleme URL’si kitap front matter’ındaki preview_link alanında tutulur. preview_link doluysa İncele butonu ve previewbook iframe görünür; boşsa görünmez. Eksik kitaplarda varsayılan desen: https://cdn.e-damla.com.tr/PUBLIC/ornek-sayfalar/{ean}/index.html; bazı kitaplarda özel path’ler (damlaegitim/, /mobile/ vb.) korunur. /urun-inceleme-linkleri sayfası preview_link dolu tüm kitapları listeler.
HDS PDF linkleri kitap front matter’ındaki tam examlink URL’si ile tanımlanır. Site genelinde _config.yml içinde examlink: true olmalıdır; aksi halde HDS butonu hiçbir kitapta görünmez. examlink: true iken book.html içinde kitap examlink doluysa HDS butonu görünür, boş veya yoksa görünmez.
examlink (HDS)
| Durum | _config.yml |
Front matter | Kitap sayfası |
|---|---|---|---|
| Site kapalı | examlink: false |
(herhangi) | HDS butonu görünmez |
| HDS yok | examlink: true |
examlink: "" |
HDS butonu görünmez |
| HDS var | examlink: true |
Tam CDN URL | HDS butonu görünür (popup iframe) |
Örnek URL: https://cdn.e-damla.com.tr/PUBLIC/hds_pdf/y/deyim-oykuleri-y.pdf
scripts/normalize_book_frontmatter.rb tüm kitaplarda examlink satırını korur; eksikse examlink: "" yazar, göreli dosya adı verilmişse tam URL’ye çevirir.
damlaurl (Satın Al / Bilgi)
| Durum | Front matter | Kitap sayfası |
|---|---|---|
| URL yok | damlaurl: "" |
Bilgi → tedarik bilgisi popup |
| URL var | Tam ürün sayfası URL | Satın Al → iframe popup |
Örnek URL: https://www.damlayayinevi.com.tr/...
scripts/normalize_book_frontmatter.rb tüm kitaplarda damlaurl satırını korur; eksikse damlaurl: "" yazar. Eski damlayayinevi front matter alanı varsa damlaurl’a taşınır. Site genelinde buyout veya damlayayinevi config ayarı yoktur.
preview_link bakımı
| Araç | Görev |
|---|---|
scripts/normalize_book_frontmatter.rb |
Front matter sıralar; preview_link, examlink, damlaurl Standart Book Attributes altında korunur; review_link → preview_link, previewpage silinir |
Front matter anahtar sırası (betik): layout, title, description, categories → ean, … → # Spesific Filterable Attributes: genre, grades, tags, degerler, anatema, egilimler, kazanim, beceriler, unite → diğer alanlar.
Yeni kitap eklerken preview_link doğrudan front matter’a yazılır; özel path gerekmezse https://cdn.e-damla.com.tr/PUBLIC/ornek-sayfalar/{ean}/index.html varsayılan desenidir.
Ürün İnceleme Linkleri Sayfası
| Özellik | Değer |
|---|---|
| Dosya | _pages/linkler.html |
| URL | /urun-inceleme-linkleri |
| Layout | page (show_title: false) |
| Veri | site.books → preview_link dolu kitaplar (sort: title) |
| Stil / JS | Sayfa içi inline <style> + <script> (include yok) |
| Footer | footer_show: true, footer_order: 15 |
Kart düzeni: solda kapak (eanimage.html), sağda sınıf rozeti + başlık + dört aksiyon — Kitabı incele (preview_link), Kitaba git (ürün URL), Whatsappda paylaş, Linki kopyala. Arama kutusu başlık ve sınıf rozeti üzerinde client-side filtre uygular (toLocaleLowerCase('tr-TR')).
Görsel tasarım design.md — Ürün İnceleme Linkleri bölümünde tanımlıdır.
Filtreleme Mantığı
Anasayfa ve /urunler aynı bileşenleri paylaşır: book-grade-nav.html + book-home-groups.html + book-filter.js
- Sınıf seçimi →
data-gradesattribute’u ile eşleşme - Tür (Eğitim/Hikaye) →
data-genreattribute’u - Hedef selector:
.listbooks-home-item - Genre grupları:
.book-genre-group(boş gruplar gizlenir)
Filtreleme tamamen client-side çalışır; Jekyll rebuild gerekmez.
Hash tabanlı paylaşılabilir URL’ler
Filtre değişince URL hash güncellenir; sayfa hash ile yüklendiğinde filtre otomatik uygulanır.
| Hash | Filtre |
|---|---|
#okul-oncesi |
Sadece sınıf (tüm türler) |
#okul-oncesi/egitim |
Sınıf + tür |
#1-sinif/hikaye |
Sınıf + tür |
Örnekler: /urunler#okul-oncesi, /urunler#1-sinif/egitim, /#2-sinif/hikaye (anasayfa)
Okul öncesi ürünler _books/ içinde henüz tam açılmamış olsa da URL yapısı hazırdır; ileride grades: [0] + genre ile eklenen ürünler ek JS değişikliği olmadan çalışır.
book-grade-filter.html eski checkbox dropdown yapısıdır; /urunler artık kullanmaz.
Jekyll Plugin’leri
Gemfile yalnızca github-pages gem’ini kullanır; aşağıdaki plugin’ler bu paketten gelir (GitHub Pages whitelist):
jekyll-feed— RSSjekyll-sitemap— sitemap.xmljekyll-seo-tag— `
` meta etiketleri (kitap sayfaları hariç; özel book-seo-tags.html)
jekyll-paginate— sayfalamajekyll-gist— Gist embed
Kaldırılan (whitelist dışı, projede kullanılmıyordu): jekyll-archives, jekyll-figure. Özel CSS assets/css/theme.css üzerinden; _sass / sass-embedded yok.
SEO ve AI Crawler Altyapısı
GitHub Pages uyumlu (özel Ruby plugin yok). Webmaster doğrulama DNS seviyesinde yapılır; kod tabanında meta tag gerekmez.
Dosya haritası
| Dosya / URL | Görev |
|---|---|
robots.txt |
Tüm crawler + AI botlara Allow; sitemap; llms hub referansı; OAI-SearchBot, Claude-SearchBot |
_pages/llms.txt |
LLM hub indeksi: özet, bölüm linkleri, öne çıkanlar, iletişim |
_pages/llms/*.txt |
Modüler bölümler — tüm kitaplar genre dosyalarında korunur |
_includes/llms-book-line.html |
Tek kitap markdown satırı (DRY) |
_includes/llms-contact.html |
llms dosyalarında ortak iletişim bloğu |
_pages/sss.md |
/sss — görünür SSS + front matter faq: |
_includes/faq-list.html |
SSS accordion / <details> render |
_includes/structured-data-faq.html |
FAQPage JSON-LD (page.faq kaynağı) |
_includes/book-seo-description.html |
Kitap meta description metni üretimi |
_includes/book-seo-tags.html |
Kitap <meta> / Open Graph / Twitter (twitter:description) |
_includes/book-minimal-content.html |
İnce gövdeli kitaplara görünür SEO paragrafı |
_includes/related-books.html |
İlgili ürünler: aynı grades+genre |
_includes/structured-data-book.html |
Product + Book + BreadcrumbList JSON-LD |
_includes/structured-data-site.html |
Organization JSON-LD; WebSite + SearchAction (/?q=) |
_includes/search-lunr.html |
Spotlight arama; site geneli ?q= init ve URL senkronu |
_includes/ai-seo-crawler.html |
Router + visually-hidden wrapper |
_includes/ai-seo-crawler-*.html |
Sayfa türüne göre LLM talimat + bağlam metni |
llms.txt modüler hub
Tek dosyada tüm kitapları listelemek yerine hub + spoke yapısı:
/llms.txt → indeks (~50 satır)
/llms/ana-sayfalar.txt → statik sayfalar
/llms/egitim-kitaplari.txt → genre == education (tüm kitaplar)
/llms/hikaye-kitaplari.txt → genre == story (tüm kitaplar)
/llms/kataloglar.txt → kataloglar
/assets/search-index.json → makine okunur tam indeks (Optional bölümde referans)
default.html head: <link rel="alternate" type="text/plain" href="/llms.txt">
Site geneli ?q= arama
| Davranış | Açıklama |
|---|---|
initFromUrlQuery() |
Herhangi bir sayfada ?q=terim → Spotlight modal + sonuçlar |
syncUrlQuery(term) |
Modal’da yazarken history.replaceState ile URL güncellenir (paylaşılabilir link) |
closeSpotlight() |
?q= silinir; hash korunur |
| Ctrl+K boş açılış | URL değişmez; yazınca ?q= eklenir |
| SearchAction schema | Kanonik: https://damlaokul.com/?q={search_term_string} |
Dinamik AI/SEO crawler içeriği
- Amaç: Öğretmen ve velilere Damla ürünlerini neden tercih etmeleri gerektiğini LLM’lere ve crawler’lara anlatan bağlamsal metin
- Format (hibrit):
data-ai-role="assistant-guidance"+data-ai-role="context-narrative"+data-ai-summary(1 cümle özet) - Sayfa türü algılama: URL + layout (
book/previewbook,/,/urunler,/kataloglar, generic) - Gizleme:
.ai-seo-crawlervisually-hidden,aria-hidden="true",data-nosnippet - Include zinciri:
_layouts/default.html→ `
` (</main> sonrası)
book-minimal-content.htmlile ilişki: Görünür SEO metni vs. gizli LLM rehberi — farklı amaç
previewbook layout
previewbook.html (~40 eğitim seti / tatil kitabı): tam ekran preview_link iframe odaklı.
- Gizli
d-noneüst hero ve yan sütun kaldırılır (ölü kod) book-minimal-content.htmlher zaman render edilir- iframe
title=" — ön okuma" structured-data-book.htmlaynı kalır
Meta description otomasyonu
Kitap sayfalarında (/urunler/*) description: front matter yoksa:
- Markdown excerpt / gövdeden (max 155 karakter)
- Yoksa şablon: ` — . sınıf kitabı. Damla Okul.`
Öncelikli kitaplara elle description: eklenebilir (SERP farklılaşması).
Schema notları
- Kitap dışı sayfalarda çift
WebSiteJSON-LD olmamalı (structured-data-site.htmlvsjekyll-seo-tag) - SSS:
page.faq→ görünür HTML +structured-data-faq.html(tek kaynak) - Anasayfada çift
<title>olmamalı (default.html+ `
`)
Yapılandırma (_config.yml)
locale: tr_TR,lang: tr,twitter.username- Kitap koleksiyonu:
lang: tr,type: product, sitemap önceliği
Build sonrası doğrulama
bundle exec jekyll build
# Temel çıktılar
test -f _site/llms.txt
test -f _site/llms/egitim-kitaplari.txt
test -f _site/llms/hikaye-kitaplari.txt
grep -c "WebSite" _site/index.html # çift schema olmamalı (1 veya jekyll-seo-tag only)
grep "twitter:description" _site/urunler/din-kulturu-ve-ahlak-bilgisi-4.html
curl -s _site/robots.txt | grep OAI-SearchBot
# llms kitap sayısı korunmuş mu
grep -c "^\-" _site/llms/egitim-kitaplari.txt
grep -c "^\-" _site/llms/hikaye-kitaplari.txt
# AI crawler bloğu
grep -l "ai-seo-crawler" _site/index.html _site/urunler.html | head -3
# Canlı önizleme (manuel)
# bundle exec jekyll serve → /?q=damla, /hakkimizda?q=matematik, ESC ile ?q= temizlenmeli
Build ve Deploy
İlk kurulum (install.sh)
Git clone sonrası Windows Git Bash / macOS / Linux:
sh install.sh
Sıra: Ruby/Bundler kontrolü → bundle install → Python fonttools → subset_font.sh → check_fonts.sh → install_image_tools.sh (Windows: winget install ImageMagick.ImageMagick, gerekirse Google.Libwebp) → _data/webp_manifest.yml → jekyll build.
Hook yok — görsel kontrol ve webp üretimi burada çalışmaz.
Yerel geliştirme (start.sh)
# Geliştirme sunucusu (hook'lar: görsel kontrol + webp üretimi)
sh start.sh # check_images.sh + generate_webp.sh + sync_site_data.py + jekyll serve
# → http://localhost:4000
start.sh önce refresh_image_paths.sh ile Windows PATH’ini düzeltir; ardından check_images.sh, generate_webp.sh ve sync_site_data.py hook’larını çalıştırır.
Canlıya alma
bundle exec jekyll build # isteğe bağlı yerel doğrulama
git add .
git commit -m "..."
git push
GitHub Pages, push sonrası kaynak branch’ten Jekyll build alır. CI/CD veya npm build yoktur. CSS değişiklikleri doğrudan assets/css/ altında yapılır ve commit edilir.
Yeni Ürün Ekleme
_books/yeni-urun.mdoluştur- Front matter doldur (
title,grades,genre,ean,preview_link,examlink,damlaurl…) preview_link— ön izleme URL’si; boş bırakılabilir (preview_link: ""→ İncele butonu görünmez)examlink— HDS PDF tam URL’si; yoksaexamlink: ""(HDS butonu görünmez)damlaurl— Damla Yayınevi ürün sayfası tam URL’si; doluysa Satın Al butonu, yoksa Bilgi → tedarik popup- İsteğe bağlı
description:— yoksa build sırasında otomatik üretilir - Kapak görselini
assets/images/ean/{ean}.jpgolarak ekle (jpg/png optimize et; webpsh start.shile otomatik) sh scripts/check_images.shile boyut kontrolü (veyash start.sh)sh start.shile kontrol etgit push
Yeni Bileşen / Stil Ekleme
- Tasarım kuralları ve dosya seçimi → design.md — Yeni Stil Ekleme Rehberi
- Partial gerekiyorsa →
_includes/altına ekle - HTML’de Bootstrap grid class’ları (
row,col-*) kullanılabilir
Harici Bağımlılıklar
| Kaynak | Kullanım |
|---|---|
cdn.e-damla.com.tr |
Kitap preview_link ön izleme sayfaları; examlink HDS PDF’leri |
feeds.behold.so |
Instagram carousel JSON feed |
script.google.com |
Öğretmen talep formu Apps Script web app (ogretmen_submit_url) |
google.com/recaptcha |
Form gönderiminde bot koruması (v2) |
| Google Analytics | G-PR1C1WGQB6 (site.google_analytics; yalnızca production) |
| Cloudflare | DNS (şu an proxy kapalı — gri bulut) |
| TurkiyeAPI / MEB | Adres ve okul JSON üretimi — getdata.md |
Font Awesome artık yerel olarak assets/fonts/fontawesome/ altından servis edilir; harici CDN kullanılmaz.
Bilinen Legacy Dosyalar
Aşağıdaki dosyalar geçmişten kalma olabilir; aktif kullanılmıyorsa temizlenebilir:
assets/js/theme.js(jQuery tabanlı;loadSearch()kullanılmıyor)assets/css/buyout.css(eski satıcı barı; hiçbir layout’ta yüklenmez)_pages/_draft/books.html