CEO Metin Yazarı

01 · Temeller

Tasarım fikri

Bir CEO ofisinin yazı masası: sakin, kesin, editoryal ve güven veren.

Kâğıt ve mürekkep

Sıcak kâğıt zemin, lacivert mürekkep. Üretilen metinler beyaz bir mektup kâğıdında, okuma serifiyle (Newsreader) durur; arayüz Instrument Sans ile konuşur.

—

İndeks sekmesi

Kısa turkuaz çizgi, bir bölümün başladığını işaret eder: sayfa başlığı, belge, istatistik, kart. Klasör ayracı gibi.

1

Tek vurgu rengi

Kuveyt Türk turkuazı yalnızca birincil eylem, etkin öğe, odak halkası ve ilerleme içindir. Turkuaz zemin üzerinde metin her zaman lacivert (AA).

◎

Guilloche

Lacivert panellerde banknot gravürünü andıran ince halka deseni. Kenar çubuğu ve yalnızca kahraman anlar için.

02 · Temeller

Renkler

Yalnızca token kullanın; ham hex yazmayın. Koyu tema aynı isimleri yeniden tanımlar.

Yüzeyler

--surface-0…Tuval / masa
--surface-1…Kartlar
--surface-2…Gömme alan
--surface-3…Ray, iskelet
--surface-raised…Kâğıt, girdi, diyalog
--surface-inverse…Ters yüzey

Mürekkep

--ink-1…Ana metin
--ink-2…İkincil metin
--ink-3…Soluk (AA)
--ink-4…Yalnız dekor
--line-2…Girdi kenarı
--line-1…Ayırıcı çizgi

Birincil · turkuaz

--primary…Dolgu
--primary-hover…Hover
--primary-ink…Metin / bağlantı
--primary-soft…Seçili zemin
--on-primary…Turkuaz üstü metin
--focus…Odak halkası

Durum

--success…Başarılı
--warning…Uyarı
--danger…Hata
--info…Bilgi
--success-soft…Başarılı zemin
--warning-soft…Uyarı zemin
--danger-soft…Hata zemin
--info-soft…Bilgi zemin

Omurga · kenar çubuğu

--side-bg…Lacivert omurga
--side-accent…Omurga vurgusu
--side-ink-2…Omurga metni

Kontrast kuralı

Turkuaz (#01b8aa) üzerinde beyaz metin AA kontrastını karşılamaz (2,5:1). Birincil butonlar bu yüzden lacivert metin kullanır (6,7:1). Turkuaz metin gerektiğinde --primary-ink kullanın.

03 · Temeller

Tipografi

Newsreader: başlıklar, sayılar, belgeler. Instrument Sans: arayüz. Her ikisi latin-ext ile Türkçe karakter destekli.

.display / page title
Newsreader 450 · clamp 30–42

Pijamalı hasta yağız şoföre çabucak güvendi.

.h2 · serif 24

Katılım bankacılığında sürdürülebilir büyüme

.h3 / .section-title · serif 20

İkinci çeyrek değerlendirmesi

.h4 · sans 15/600

Kaynaklar ve veri dönemi

.eyebrow · 11 caps

Bilgi kaynakları · İstikrar

.lead · 17

Taslağınızı verin; üslup, veri ve kalite ajanları CEO'nun sesiyle yeniden yazsın.

body · sans 15

Arayüz metni 15 piksel, 1,55 satır aralığıyla okunur. Türkçe karakterler: İ ı Ğ ğ Ş ş Ç ç Ö ö Ü ü Â â.

.text-sm / .text-muted

Yardımcı metin, meta bilgi ve açıklamalar için.

code / .num

TP.DK.USD.A · 1.234.567,89 TL

.prose-doc · serif 18/1.7

Değerli paydaşlarımız, zorlu bir küresel konjonktürde güçlü sermaye yapımız ve ilkeli bankacılık anlayışımızla istikrarlı bir yıl geride bıraktık.

04 · Temeller

Boşluk, köşe, gölge, hareket

4 px tabanlı boşluk; kesin, küçük köşe yarıçapları; lacivert tonlu katmanlı gölgeler.

Boşluk ölçeği

--space-*

--space-14 px
--space-28 px
--space-312 px
--space-416 px
--space-520 px
--space-624 px
--space-832 px
--space-1040 px
--space-1248 px
--space-1664 px

Köşe yarıçapları

--radius-*

xs
sm
md
lg
xl
pill

Gölgeler

--shadow-*

--shadow-1
--shadow-2
--shadow-3
--shadow-sheet

Hareket

Süreler ve eğriler

--dur-1 · 120msHover, basış
--dur-2 · 200msKüçük durum değişimleri
--dur-3 · 320msÇekmece, diyalog, bildirim
--dur-4 · 560msSayfa girişi (kademeli)
--ease-outVarsayılan giriş eğrisi

prefers-reduced-motion: animasyonlar kapanır; yalnızca durum bildiren döner göstergeler yavaşça döner.

Katmanlar

--z-*

--z-sticky · 10Yapışkan başlıklar
--z-topbar · 30Mobil üst çubuk
--z-backdrop · 40Çekmece arka planı
--z-drawer · 50Kenar çubuğu
--z-modal · 60 / --z-toast · 70 / --z-tooltip · 80Diyalog, bildirim, ipucu

05 · Temeller

İkonlar

24 px ızgarada çizgi ikonlar (Lucide çizgisinde), currentColor. Kopyalamak için tıklayın.

Kullanım

icon(name, size=18, cls="", label=None, stroke=None)

Kaynak
{{ ui.icon("feather") }}                    {# 18px, dekoratif (aria-hidden) #}
{{ ui.icon("check", 14, "text-success") }}  {# boyut + ek sınıf #}
{{ ui.icon("info", 20, label="Bilgi") }}    {# anlamlı ikon → role="img" #}

06 · Düzen

Düzen

Uygulama kabuğu, sayfa başlığı, ızgaralar, yığınlar ve iki bölmeli editör.

Sayfa şablonu

base.html blokları: title · head · page_class · page_header · content · scripts

Kenar çubuğu 1024 px altında çekmeceye dönüşür; üstte menü düğmeli bir çubuk belirir.

Kaynak
{% extends "base.html" %}
{% import "components/ui.html" as ui %}
{% set nav_active = "history" %}  {# opsiyonel: menüde etkin öğeyi zorla #}

{% block title %}Geçmiş — CEO Metin Yazarı{% endblock %}
{% block page_class %}page--wide{% endblock %}   {# page--narrow | page--static #}

{% block page_header %}
  {% call ui.page_header("Geçmiş", "Oluşturulan tüm metinler.", eyebrow="Arşiv") %}
    {{ ui.button("Yeni Metin", icon="feather", href="/compose") }}
  {% endcall %}
{% endblock %}

{% block content %} … {% endblock %}
{% block scripts %}<script>/* App ve htmx burada hazır */</script>{% endblock %}

Sayfa başlığı

page_header(title, subtitle=None, eyebrow=None, icon=None) · çağrı bloğu → eylemler

Kaynak
{% call ui.page_header("Stil Arşivi", "Üslup örneği olarak kullanılan CEO metinleri.", eyebrow="Bilgi Kaynakları", icon="library") %}
  {{ ui.button("Metin Yükle", icon="upload", data_modal_open="upload-modal") }}
  {{ ui.button("Dışa aktar", "secondary", icon="download") }}
{% endcall %}

Bölüm başlığı

section_head(title, subtitle=None, eyebrow=None, level=2) · çağrı → eylemler

Arşiv

Son metinler

Bu hafta oluşturulanlar

Kaynak
{% call ui.section_head("Son metinler", "Bu hafta oluşturulanlar", eyebrow="Arşiv") %}
  {{ ui.button("Tümü", "ghost", icon_right="arrow-right", href="/history", size="sm") }}
{% endcall %}

Izgaralar

.grid · .grid-2 · .grid-3 · .grid-4 · .grid-auto · .col-span-full

1
2
3
.col-span-full
Kaynak
<div class="grid-3"> … </div>        {# 1 → 2 (≥640) → 3 (≥1100) sütun #}
<div class="grid-2"> … </div>        {# 1 → 2 (≥768) #}
<div class="grid-4"> … </div>        {# 1 → 2 → 4 · istatistik satırları #}
<div class="grid-auto" style="--grid-min: 14rem"> … </div>
{# aralık: .grid--tight · .grid--loose · --grid-gap #}

Yığın ve küme

.stack · .stack-sm/lg/xl · .cluster (+ --between, --end, --center, --tight, --loose, --top, --nowrap)

stack-sm
…
…
clustersarmalıortalıboşluklukümeler
Kaynak
<div class="stack"> … </div>       {# dikey, 16px · .stack-sm 8 · .stack-lg 32 · .stack-xl 48 #}
<div class="cluster"> … </div>     {# yatay, sarmalı, ortalı · --between --end --tight --loose #}

İki bölmeli editör

.split · .split__pane · .sticky-panel

split__pane · form
split__pane · .sticky-panel
Kaynak
<div class="split">                       {# 5fr / 7fr ≥1100px; altında alt alta #}
  <div class="split__pane"> …taslak formu… </div>
  <div class="split__pane">
    <div class="sticky-panel"> …sonuç / canlı önizleme… </div>
  </div>
</div>
{# varyantlar: .split--even · .split--aside (içerik + 21rem) · .split--aside-left #}

07 · Bileşenler

Butonlar

Sayfa başına tek birincil eylem. İkon yalnız butonlarda aria_label zorunlu (ipucu olarak da gösterilir).

Varyantlar

button(label, variant="primary", size=None, icon=None, icon_right=None, href=None, type="button", **kw)

Kaynak
{{ ui.button("Metni Oluştur", icon="sparkles") }}
{{ ui.button("Taslak Kaydet", "secondary", icon="save") }}
{{ ui.button("Vazgeç", "ghost") }}
{{ ui.button("Yeniden Dene", "subtle", icon="refresh") }}
{{ ui.button("Sil", "danger", icon="trash") }}
{{ ui.button("Kaldır", "danger-ghost", icon="trash") }}

Boyutlar ve ikon butonlar

size: sm | lg · label=None → kare ikon buton · block=True

Kaynak
{{ ui.button("Küçük", size="sm") }}
{{ ui.button("Orta") }}
{{ ui.button("Büyük eylem", size="lg", icon_right="arrow-right") }}
{{ ui.button(None, "secondary", icon="copy", aria_label="Kopyala") }}
{{ ui.button(None, "ghost", icon="more-horizontal", size="sm", aria_label="Diğer") }}
{{ ui.button("Tam genişlik", "secondary", block=True) }}

Durumlar

busy · disabled · hx-confirm → stilli onay diyaloğu

Kaynak
{{ ui.button("Oluşturuluyor", busy=True) }}          {# aria-busy="true" → döner gösterge #}
{{ ui.button("Devre dışı", "secondary", disabled=True) }}
{{ ui.button("Sil", "danger", icon="trash",
             hx_delete="/jobs/123", hx_confirm="Bu metin kalıcı olarak silinsin mi?",
             data_confirm_ok="Sil") }}
{# htmx isteği sürerken: .btn.htmx-request ve form.htmx-request .btn[type=submit]
   otomatik olarak meşgul görünür (kapatmak için .btn--no-busy) #}

Grup ve kopyalama

.btn-group · copy_button(target, label="Kopyala", variant="secondary", size="sm", message=None)

Kopyalanan örnek metin.
Kaynak
<div class="btn-group">
  {{ ui.button("TR", "secondary", size="sm") }}{{ ui.button("EN", "secondary", size="sm") }}
</div>
{{ ui.copy_button("#metin", "Kopyala") }}      {# data-copy-target + ✓ geri bildirimi #}
<button class="btn btn--ghost" data-copy="Sabit metin">Sabit metni kopyala</button>

08 · Bileşenler

Rozetler

Durum ve kategori etiketleri. Tonlar anlam taşır; süs için kullanmayın.

Tonlar

badge(text, tone="neutral", icon=None, dot=False, pulse=False) · status_badge(status)

Röportaj Stil örneği Geçti Gözden geçir Hata EVDS v3 Taslak
SıradaÇalışıyorTamamlandıBaşarısızİptal edildiYarıda kaldıAtlandı
Kaynak
{{ ui.badge("Röportaj") }}
{{ ui.badge("Stil örneği", "primary", icon="library") }}
{{ ui.badge("Geçti", "success", dot=True) }}
{{ ui.badge("Gözden geçir", "warning") }}
{{ ui.badge("Hata", "danger") }}
{{ ui.badge("EVDS", "info") }}
{{ ui.badge("v3", "solid") }}
{{ ui.badge("Taslak", "outline") }}
{{ ui.status_badge("running") }}   {# queued · running · completed · failed · cancelled · interrupted · skipped #}

09 · Bileşenler

Kartlar

Beyaz yaprak, kıl çizgisi, hafif gölge. Eylemler ve alt bilgi {% set %} ile işaretleme olarak geçer.

Başlık, eylemler, alt bilgi

card(title=None, subtitle=None, icon=None, actions=None, footer=None, href=None, flush=False, level=3, **kw)

Veri dönemi

EVDS göstergeleri bu döneme göre çekilir.

Kaynak
{% set acts %}
  {{ ui.button(None, "ghost", icon="refresh", size="sm", aria_label="Yenile") }}
{% endset %}
{% set foot %}
  {{ ui.button("İptal", "ghost", size="sm") }}{{ ui.button("Kaydet", size="sm") }}
{% endset %}
{% call ui.card("Veri dönemi", "EVDS göstergeleri bu döneme göre çekilir.", icon="calendar",
                actions=acts, footer=foot) %}
  … gövde …
{% endcall %}

Bağlantı kartı ve varyantlar

href → tüm kart tıklanabilir (card--interactive). İç içe bağlantı koymayın.

Finansal Raporlar

12 rapor · son: 2026 Q2

Bilanço ve gelir tablosu özetleri.

Gömme kart

card--inset

Kart içindeki ikincil alanlar için.

Sekmeli kart

card--tabbed

Üstte turkuaz indeks sekmesi.

Lacivert panel

card--ink · kahraman anlar

Kaynak
{% call ui.card("Finansal Raporlar", "12 rapor · son: 2026 Q2", icon="landmark", href="/financial") %}
  <p class="text-sm text-muted">Bilanço ve gelir tablosu özetleri.</p>
{% endcall %}
{# class_ ile: card--inset · card--flat · card--tabbed · card--ink #}

10 · Bileşenler

Formlar

Her kontrol bir field() içinde: etiket, ipucu/hata, sayaç. id varsayılan olarak name ile aynıdır.

Girdi

field(label, for_=None, hint=None, required=False, error=None, counter=False, optional=False, group=False, aside=None) · input(name, value="", placeholder="", type="text", **kw)

Geçmiş listesinde görünür.

Geçerli bir e-posta adresi girin.

Kaynak
{% call ui.field("Metin başlığı", for_="title", hint="Geçmiş listesinde görünür.", required=True) %}
  {{ ui.input("title", placeholder="Örn. 2026 ara dönem röportajı", required=True) }}
{% endcall %}

{% call ui.field("Ara", for_="q") %}
  {{ ui.input("q", type="search", placeholder="Metinlerde ara…", icon="search") }}
{% endcall %}

{% call ui.field("E-posta", for_="email", error="Geçerli bir e-posta adresi girin.") %}
  {{ ui.input("email", "ceo@", type="email", invalid=True) }}
{% endcall %}

Metin alanı (belge yazımı)

textarea(name, value="", rows=6, placeholder="", **kw) · doc=True serif · autosize=True · counter=True

En az 10 karakter. Soruları numaralandırabilirsiniz.

Kaynak
{% call ui.field("Taslak metin", for_="base_text", required=True, counter=True,
                 hint="En az 10 karakter. Soruları numaralandırabilirsiniz.") %}
  {{ ui.textarea("base_text", rows=8, doc=True, autosize=True, counter=True,
                 placeholder="CEO taslağını veya röportaj sorularını yapıştırın…",
                 required=True, minlength=10) }}
{% endcall %}
{# counter="words" | "chars" · data_autosize_max="560" · maxlength → "12 / 500 karakter" #}

Seçim kutusu

select(name, options, selected=None, **kw) · options: (value, label) | {value, label, disabled} | {label, options:[…]} · placeholder=

Kaynak
{% call ui.field("Metin türü", for_="text_type") %}
  {{ ui.select("text_type", [("roportaj", "Röportaj / Soru-Cevap"), ("basin", "Basın bülteni"),
                             ("konusma", "Konuşma metni"), ("mektup", "Yıllık mektup")],
               selected="roportaj") }}
{% endcall %}
{# gruplu seçenekler #}
{{ ui.select("model", [{"label": "Google Gemini", "options": [("google/gemini-3.8-flash", "Gemini 3.8 Flash")]},
                       {"label": "OpenAI", "options": [("openai/gpt-6-sol", "GPT-6 Sol")]}],
             placeholder="Model seçin…") }}

Çipler

chips(name, options, selected=None, multiple=False, **kw) · radyo (tekli) veya onay kutusu (çoklu)

Çıktı dili

Birden fazla seçebilirsiniz.

Ton
Kaynak
{% call ui.field("Çıktı dili", group=True, hint="Birden fazla seçebilirsiniz.") %}
  {{ ui.chips("languages", [("tr", "Türkçe"), ("en", "English")], selected=["tr"], multiple=True) }}
{% endcall %}
{% call ui.field("Ton", group=True) %}
  {{ ui.chips("tone", [{"value": "resmi", "label": "Resmî", "icon": "landmark"},
                       {"value": "sicak", "label": "Sıcak"}, {"value": "vizyoner", "label": "Vizyoner"}],
              selected="resmi") }}
{% endcall %}

Segment, anahtar, onay

segmented(name, options, selected=None, **kw) · switch(name, label, checked=False, hint=None, **kw) · checkbox(name, label, checked=False, value=None, hint=None)

Kaynak
{{ ui.segmented("period_type", [("monthly", "Aylık"), ("quarterly", "Çeyreklik"),
                                ("yearly", "Yıllık")], selected="monthly", label="Dönem türü") }}

{{ ui.switch("use_financial", "Finansal raporları kullan", checked=True,
             hint="Son yüklenen bilanço özeti metne eklenir.") }}
{{ ui.checkbox("remember", "Bu ayarları hatırla") }}

Form düzeni

.form-grid (--2) · .form-actions (--between) · form[data-shortcut-submit] → Ctrl/⌘+Enter

CtrlEnter ile gönder
Kaynak
<form class="stack-lg" hx-post="/compose" data-shortcut-submit>
  <div class="form-grid form-grid--2"> …alanlar… </div>
  <div class="form-actions">
    <span class="hint mr-auto">{{ ui.kbd("Ctrl+Enter") }} ile gönder</span>
    {{ ui.button("Vazgeç", "ghost", href="/") }}
    {{ ui.button("Metni Oluştur", type="submit", icon="sparkles") }}
  </div>
</form>

11 · Bileşenler

Dosya yükleme

Gerçek dosya girdisi alanı kaplar: tıklama, klavye ve sürükle-bırak yerel olarak çalışır.

Sürükle-bırak alanı

dropzone(name="file", accept=".pdf,.docx,.txt,.xlsx", hint=None, multiple=False, **kw) · compact=True · title=

Kaynak
<form hx-post="/upload/file" hx-encoding="multipart/form-data" hx-target="#upload-result">
  {{ ui.dropzone("file") }}                                   {# .pdf,.docx,.txt,.xlsx #}
  {{ ui.dropzone("files", accept=".pdf,.docx", multiple=True, compact=True,
                 hint="PDF veya DOCX · birden fazla dosya") }}
</form>

12 · Bileşenler

Sekmeler

ARIA sekmeleri; ok tuşları, Home/End. Başlangıçta görünmeyen panelleri hidden ile işaretleyin.

Alt çizgili sekmeler

tabs(items, active=None, id=None, persist=None, variant=None, label=None) · tab_panel(key, active=False, tabs_id=None)

Değerli paydaşlarımız, istikrarlı büyümemizi sürdürüyoruz.

Kaynak
{% call ui.tabs([{"key": "tr", "label": "Türkçe", "icon": "file-text"},
                {"key": "en", "label": "English"},
                {"key": "diff", "label": "Karşılaştır", "icon": "git-compare", "count": 3}],
               active="tr", id="result-tabs", persist="lang", label="Dil") %}
  {% call ui.tab_panel("tr", active=True) %} … {% endcall %}
  {% call ui.tab_panel("en") %} … {% endcall %}
  {% call ui.tab_panel("diff") %} … {% endcall %}
{% endcall %}
{# JS: App.tabs.activate("#result-tabs", "en") · olay: "tabs:change" (kök), "tab-shown" (panel)
   htmx tembel yükleme: <div data-tab-panel="diff" hx-get="…" hx-trigger="tab-shown once"> #}

Hap sekmeler

variant="pills" — araç çubukları için

Önizleme paneli.

Kaynak
{% call ui.tabs([("preview", "Önizleme"), ("source", "Kaynak"), ("notes", "Notlar")],
               variant="pills", label="Görünüm") %}
  {% call ui.tab_panel("preview", active=True) %} … {% endcall %}
  …
{% endcall %}

13 · Bileşenler

Adım göstergesi

Ajan hattının canlı ilerlemesi. Durum data-status özniteliğindedir; SSE olayı geldiğinde tek öznitelik değişir.

Dikey zaman çizelgesi

stepper(steps, horizontal=False, **kw) · durumlar: pending · running · completed · failed · skipped

  1. Kaynaklar(Tamamlandı) 4 stil örneği · 9 gösterge · 1,2 sn
  2. Taslak(Tamamlandı) Gemini 3.1 Pro · 18,4 sn
  3. Veri(Çalışıyor) Gemini 3.8 Flash · akış sürüyor…
  4. Kalite(Bekliyor)
  5. Düzeltme(Atlandı) Puan eşiği aşıldı
  6. Çeviri (EN)(Bekliyor)
Kaynak
{{ ui.stepper([
     {"key": "context", "label": "Kaynaklar", "status": "completed", "meta": "4 stil örneği · 1,2 sn"},
     {"key": "draft",   "label": "Taslak",    "status": "completed", "meta": "Gemini 3.1 Pro · 18,4 sn"},
     {"key": "enrich",  "label": "Veri",      "status": "running"},
     {"key": "review",  "label": "Kalite",    "status": "pending"},
     {"key": "revise",  "label": "Düzeltme",  "status": "skipped", "meta": "Puan eşiği aşıldı"},
   ], id="job-steps") }}
{# job.stages (StageRecord listesi) doğrudan verilebilir — Enum durumlar desteklenir #}

<script>
  App.updateStep("#job-steps", "enrich", { status: "completed", meta: "9,8 sn" });
  App.updateStep("#job-steps", "review", { status: "running" });
</script>

Yatay (kompakt başlık)

horizontal=True — iş sayfasının üst şeridi için

  1. Kaynaklar(Tamamlandı) 4 stil örneği · 9 gösterge · 1,2 sn
  2. Taslak(Tamamlandı) Gemini 3.1 Pro · 18,4 sn
  3. Veri(Çalışıyor) Gemini 3.8 Flash · akış sürüyor…
  4. Kalite(Bekliyor)
  5. Düzeltme(Atlandı) Puan eşiği aşıldı
Kaynak
{{ ui.stepper(steps, horizontal=True) }}

14 · Bileşenler

Kalite puanı

Ton puandan gelir: ≥80 başarılı · 70–79 birincil · 50–69 uyarı · <50 hata. Kadranın dış halkası gravür çentikleridir.

Puan kadranı

score_ring(score, size=96, label=None) · App.setScore(el, score)

Kalite
Kaynak
{{ ui.score_ring(92) }}
{{ ui.score_ring(76, label="Kalite") }}
{{ ui.score_ring(61, size=72) }}
{{ ui.score_ring(38, size=72) }}
{{ ui.score_ring(None, size=72) }}          {# puan yok → "—" #}
<script>App.setScore(".score-ring", 84)</script>

Alt puan çubukları

score_bar(label, value, max) — değer Türkçe biçimlenir (18,5)

Üslup34 / 40
Veri doğruluğu18,5 / 25
Profesyonel ton9 / 10
Akıcılık12 / 25
Kaynak
<div class="score-bars">
  {{ ui.score_bar("Üslup", 34, 40) }}
  {{ ui.score_bar("Veri doğruluğu", 18.5, 25) }}
  {{ ui.score_bar("Profesyonel ton", 9, 10) }}
  {{ ui.score_bar("Akıcılık", 12, 25) }}
</div>

15 · Bileşenler

İstatistik kutuları

Serif rakamlar, tablo hizası. Ton, üstteki indeks sekmesini renklendirir.

Kutular

stat(label, value, hint=None, icon=None, tone=None, unit=, delta=, delta_dir="up"|"down", href=)

Oluşturulan metin
128
Son 30 gün
Ortalama puan
84/ 100+3,2
USD/TRY
41,27%0,4
TCMB alış · örnek veri
Stil arşivi
36metin
Kaynak
<div class="grid-4">
  {{ ui.stat("Oluşturulan metin", "128", hint="Son 30 gün", icon="file-text", tone="primary") }}
  {{ ui.stat("Ortalama puan", "84", unit="/ 100", delta="+3,2", delta_dir="up", tone="success") }}
  {{ ui.stat("USD/TRY", "41,27", delta="%0,4", delta_dir="down", hint="TCMB alış · 23.09.2026") }}
  {{ ui.stat("Stil arşivi", "36", unit="metin", href="/upload", icon="library") }}
</div>

16 · Bileşenler

Uyarılar

Sayfa içi kalıcı mesajlar. Geçici geri bildirim için bildirim (toast) kullanın.

Tonlar

alert(message, tone="info", title=None, dismissible=False) · çağrı → ek içerik · tone: info | success | warning | danger (error) | primary | neutral

Ekonomik veri kapalı

EVDS anahtarı tanımlı değil; ekonomik veriler eklenmeyecek.

Metin kalite eşiğini geçti.

Önceki denemelerden daha iyi puan alan sürüm korunur.

Taslak otomatik kaydedildi.

Kaynak
{{ ui.alert("EVDS anahtarı tanımlı değil; ekonomik veriler eklenmeyecek.", "warning",
            title="Ekonomik veri kapalı") }}
{{ ui.alert("Metin kalite eşiğini geçti.", "success") }}
{{ ui.alert("Model yanıt vermedi.", "danger", title="Oluşturma başarısız", dismissible=True) }}
{% call ui.alert("Önceki denemelerden daha iyi puan alan sürüm korunur.", "info") %}
  {{ ui.button("Denemeleri gör", "secondary", size="sm") }}
{% endcall %}

17 · Bileşenler

Boş durum

Mühür halkalı ikon, serif başlık ve bir sonraki adım.

Boş durum

empty_state(icon, title, body=None, compact=False) · çağrı → eylemler

Henüz metin yok

İlk taslağınızı verin; üslup, veri ve kalite ajanları gerisini halletsin.

Kaynak
{% call ui.empty_state("inbox", "Henüz metin yok",
                       "İlk taslağınızı verin; üslup, veri ve kalite ajanları gerisini halletsin.") %}
  {{ ui.button("Yeni Metin", icon="feather", href="/compose") }}
{% endcall %}
{{ ui.empty_state("search", "Sonuç bulunamadı", compact=True) }}

18 · Bileşenler

Yükleniyor

İskelet, döner gösterge, ilerleme çubuğu ve htmx göstergeleri.

Göstergeler

skeleton(lines=3, title=False) · spinner(size="md", label="Yükleniyor…") · progress(value=None, max=100, label=None)

Yükleniyor…
Yükleniyor… Yükleniyor… Yükleniyor… Yükleniyor… Kaynaklar toplanıyor…
Kaynak
{{ ui.skeleton(4, title=True) }}
{{ ui.spinner("sm") }} {{ ui.spinner() }} {{ ui.spinner("lg") }}
{{ ui.progress(62, label="Yükleme") }}
{{ ui.progress(label="Hazırlanıyor") }}     {# value=None → belirsiz #}
<span class="loading-inline">{{ ui.spinner("sm") }} Kaynaklar toplanıyor…</span>

htmx göstergeleri

.htmx-indicator · .htmx-indicator--block · .htmx-hide-on-request · .dim-on-request · .swap-fade

İstek sırasında gösterge görünür.
Kaynak
<button class="btn btn--secondary" hx-get="/api/economic" hx-target="#eco">
  Yenile <span class="htmx-indicator">{{ ui.spinner("sm") }}</span>
</button>
<div id="eco" class="swap-fade">…</div>    {# .swap-fade: yeni içerik yumuşakça belirir #}
{# .htmx-indicator: istek sürerken görünür (inline-flex) · --block varyantı
   .htmx-hide-on-request · .dim-on-request #}

19 · Bileşenler

Tablo

Yapışkan başlık, satır vurgusu, sayılar sağa hizalı. Geniş tablolar .table-wrap içinde kayar.

Tablo

.table-wrap (--table-max-h) · .table · .num · .actions · .table__primary/__sub · .table--compact

BaşlıkTürPuanDurumTarihEylemler
Ara dönem röportajıTR · EN Röportaj 86 Tamamlandı
Sürdürülebilirlik raporu önsözüTR Yıllık mektup 74 Tamamlandı
KOBİ finansmanı basın bülteniTR Basın bülteni 58 Yarıda kaldı
Yıl sonu çalışan mesajıTR Konuşma — Çalışıyor
Yatırımcı günü açılış konuşmasıEN Konuşma 41 Başarısız
Kaynak
<div class="table-wrap" style="--table-max-h: 22rem">
  <table class="table">
    <thead><tr><th>Başlık</th><th>Tür</th><th class="num">Puan</th><th>Durum</th><th>Tarih</th><th class="actions"></th></tr></thead>
    <tbody>
      <tr>
        <td><span class="table__primary">Ara dönem röportajı</span><span class="table__sub">TR · EN</span></td>
        <td>{{ ui.badge("Röportaj") }}</td>
        <td class="num">86</td>
        <td>{{ ui.status_badge("completed") }}</td>
        <td>{{ ui.time_ago(job.created_at) }}</td>
        <td class="actions">{{ ui.button(None, "ghost", icon="eye", size="sm", aria_label="Aç", href="/jobs/…") }}</td>
      </tr>
    </tbody>
  </table>
</div>

20 · Bileşenler

Liste satırları

Tüm satır bağlantıdır (başlık bağlantısı gerilir); eylemler üstte tıklanabilir kalır. Önizleme serif ve 2 satır.

Liste

list_row(title, meta=None, href=None, icon=None, preview=None, aside=None) · çağrı → eylemler

Stratejik Plan 2026–2028.pdf

42 sayfaYüklendi 12.09.2026

İş kimliği
7f3c9a2e
Toplam süre
48,2 sn
Tahmini maliyet
0,0184 $
Kaynak
<div class="list">
  {% call ui.list_row("Ara dönem röportajı", href="/jobs/abc", icon="mic",
                      preview=job.preview, meta=["Röportaj", "TR · EN", "Puan 86"],
                      aside=ui.time_ago(job.created_at)) %}
    {{ ui.button(None, "ghost", icon="trash", size="sm", aria_label="Sil",
                 hx_delete="/history/abc", hx_confirm="Silinsin mi?") }}
  {% endcall %}
</div>
{# .list--flush: kart içinde kenarsız · .dl: anahtar/değer listesi #}

22 · Bileşenler

Bildirimler

Geçici geri bildirim. Sunucudan HX-Trigger ile, istemciden App.toast ile.

Bildirim tonları

App.toast(message, tone="info", {title, timeout}) · tone: success | info | warning | danger (error) | primary

Kaynak
# Python (router) — app.core.templating.toast_headers
return templates.TemplateResponse(request, "partials/…", ctx,
                                  headers=toast_headers("Metin kaydedildi.", "success"))
# → HX-Trigger: {"toast": {"message": "Metin kaydedildi.", "tone": "success"}}

// JavaScript
App.toast("Metin kaydedildi.", "success");
App.toast("Bağlantı koptu.", "danger", { title: "Hata", timeout: 0 });   // 0 = kalıcı
// htmx hataları (4xx/5xx, ağ, zaman aşımı) otomatik olarak Türkçe hata bildirimi olur;
// FastAPI {"detail": "..."} mesajı varsa o gösterilir. Susturmak için: data-silent-errors

23 · Bileşenler

İpucu ve klavye

Yalnızca CSS ipucu (350 ms gecikme); temel bilgiyi ipucuna saklamayın.

İpucu ve tuş

[data-tooltip] + data-tooltip-pos="bottom|left|right" · kbd(keys)

CtrlEnter ⌘K Esc
Kaynak
<button class="btn btn--secondary" data-tooltip="Panoya kopyala">Üstte</button>
<button class="btn btn--secondary" data-tooltip="Aşağıda" data-tooltip-pos="bottom">Altta</button>
<span data-tooltip="Soldaki ipucu" data-tooltip-pos="left">…</span>   {# right da var #}

{{ ui.kbd("Ctrl+Enter") }}  {{ ui.kbd(["⌘", "K"]) }}  {{ ui.kbd("Esc") }}

24 · İçerik

Belge ve metin

Üretilen CEO metinleri kahramandır: beyaz mektup kâğıdı, serif, 68 karakter ölçü, soru numaraları kenarda asılı.

Mektup kâğıdı + doc()

sheet(eyebrow=None, meta=None, stacked=False) · doc(text, letter=False) · .prose-doc (--pre, --compact, --wide, --letter)

Röportaj · Türkçe · v2

2026 Ara Dönem Değerlendirmesi

1.Yılın ilk yarısını nasıl değerlendiriyorsunuz?

Küresel belirsizliklerin sürdüğü bir dönemde, katılım bankacılığının güçlü ilkelerine bağlı kalarak istikrarlı bir büyüme performansı sergiledik. Müşterilerimizin güvenini şeffaf ve sorumlu bankacılık anlayışımızla pekiştirmeye devam ediyoruz.

2.Dijital dönüşümde öncelikleriniz neler?

Önceliğimiz; müşteri deneyimini sadeleştiren, güvenliği artıran ve çalışma arkadaşlarımızın verimliliğini yükselten çözümler. Yapay zekâ destekli süreçlerle hizmet kalitemizi ölçülebilir biçimde iyileştiriyoruz.

  • Mobil ve internet şubede uçtan uca dijital süreçler
  • KOBİ'ler için hızlı finansman erişimi

Önümüzdeki dönemde de sürdürülebilir finansmana, üretimin ve ihracatın gücüne destek olmayı sürdüreceğiz.

Kaynak
{% set meta %}{{ ui.time_ago(job.created_at) }}{% endset %}
{% call ui.sheet(eyebrow="Röportaj · Türkçe · v2", meta=meta, stacked=True) %}
  {{ ui.doc(job.text("tr")) }}
{% endcall %}

{# doc(): düz metni biçimler — "# başlık", "1. …?" soru, "- " madde, "---" süs
   ham satır sonları için: <div class="prose-doc prose-doc--pre">{{ text }}</div>
   mektup açılışı (büyük ilk harf): ui.doc(text, letter=True) #}

Fark ve kalite işaretleri

ins.diff-add · del.diff-del · mark.issue-mark--yuksek|orta|dusuk (+ .is-active)

Toplam aktiflerimiz güçlü bir şekilde istikrarlı biçimde büyüdü.

Katılım fonlarımız rekor seviyede arttı; bu muazzam başarı bir de ekibimizin eseri.

Kaynak
<p>Toplam aktiflerimiz <del class="diff-del">güçlü bir şekilde</del>
   <ins class="diff-add">istikrarlı biçimde</ins> büyüdü.</p>

<p>… <mark class="issue-mark issue-mark--yuksek" data-tooltip="Doğrulanamayan veri">rekor seviyede</mark> …
   <mark class="issue-mark issue-mark--orta">muazzam</mark> …
   <mark class="issue-mark issue-mark--dusuk">bir de</mark> …</p>
{# önem: yuksek | orta | dusuk (high | medium | low) · .is-active vurgusu #}

Kalite bulguları

issue(description, severity="orta", quote=None, suggestion=None, category=None)

Yüksek önemVeri doğruluğu
“Katılım fonlarımız rekor seviyede arttı”

Rakam kaynaklarla doğrulanamıyor; genel bir ifade tercih edin.

“Katılım fonlarımız istikrarlı biçimde arttı” olarak yumuşatın.

Orta önemÜslup
“muazzam başarı”

Abartılı sıfat CEO üslubuna uymuyor.

Düşük önemAkıcılık

Küçük akıcılık iyileştirmesi.

“bir de” ifadesini çıkarın.

Kaynak
{% for i in job.quality.issues %}
  {{ ui.issue(i.description, i.severity, quote=i.quote, suggestion=i.suggestion, category=i.category) }}
{% endfor %}

Canlı yazım imleci

.stream-caret · .stream-caret--after — akış metni için yanıp sönen turkuaz imleç

Değerli paydaşlarımız,
Kaynak
<div class="prose-doc prose-doc--pre is-streaming" id="live">
  <span data-live-text></span><span class="stream-caret" aria-hidden="true"></span>
</div>
<script>
  // SSE "delta" olayı: metni imleçten önce ekleyin
  liveText.append(document.createTextNode(evt.text));
</script>
{# alternatif: kapsayıcıya .stream-caret--after sınıfı #}

25 · Yardımcılar

Yardımcı sınıflar

Küçük ve bilinçli bir set. Yeni yardımcı sınıf eklemeden önce bileşenlere bakın.

SınıfEtki
.text-ink / -subtle / -mutedMürekkep tonları (ink-1 / ink-2 / ink-3)
.text-primary / -success / -warning / -danger / -infoAnlamsal metin renkleri (AA)
.text-2xs … .text-2xlTip ölçeği (11–24 px)
.font-serif / -sans / -mono · .font-medium / -semibold · .italicYazı tipi
.numTablo rakamları (tabular-nums)
.eyebrow (--primary) · .lead · .display · .h1–.h4Tipografi rolleri
.truncate · .line-clamp-2/3 · .nowrap · .pre-wrap · .break-wordsMetin taşması
.hidden · .visually-hidden (.sr-only) · .hide-mobile · .show-mobileGörünürlük
.flex · .inline-flex · .flex-col · .flex-wrap · .flex-1 · .shrink-0 · .min-w-0 · .w-fullEsnek kutu
.items-start/center/end/baseline · .justify-start/center/end/between · .self-start/center · .ml-auto · .mx-autoHizalama
.gap-1/2/3/4/6Boşluk (4–24 px)
.m-0 · .mt-0/1/2/3/4/6/8/12 · .mb-0/1/2/3/4/6/8 · .p-0/4/6Dış / iç boşluk (birkaç adım)
.text-left / -center / -rightMetin hizası
.surface-1 / -2 · .rounded · .bordered · .surface-ink · .guillocheYüzey yardımcıları
.meta · .toolbar (__sep, __spacer) · .divider (--label) · .disclosure · .icon-tile · .avatar · .dlKüçük desenler
.fade-in · .reveal-children · .flash · .page--staticHareket

Küçük desenler

.meta · .toolbar · .disclosure · .divider--label

RöportajTR · EN
v3
Taslak (ajan 1)
Rötuş öncesi ilk taslak burada görünür.
Veri zenginleştirme (ajan 2)
Eklenen göstergeler ve kaynaklar.
Kaynak
<div class="meta"><span>Röportaj</span><span>TR · EN</span><span>{{ ui.time_ago(job.created_at) }}</span></div>

<div class="toolbar">
  {{ ui.button("Kopyala", "ghost", icon="copy", size="sm") }}
  <span class="toolbar__sep"></span>
  {{ ui.button("DOCX", "ghost", icon="download", size="sm") }}
  <span class="toolbar__spacer"></span>
  {{ ui.badge("v3") }}
</div>

<details class="disclosure">
  <summary>Ara çıktılar</summary>
  <div class="disclosure__body">…</div>
</details>

26 · Entegrasyon

htmx kalıpları

htmx 2.0.11 · hata bildirimleri, onay diyaloğu ve 422 form yeniden çizimi app.js'te hazır.

KalıpDavranış
HX-Trigger: {"toast": {...}}Sunucu bildirimi. toast_headers(message, tone) üretir.
hx-confirm="…"Stilli onay diyaloğu. Özelleştirme: data-confirm-title, data-confirm-ok, data-confirm-tone="danger" (DELETE isteklerinde varsayılan).
data-confirm="…"htmx olmayan bağlantı, buton ve formlar için aynı diyalog.
4xx / 5xx yanıtTürkçe hata bildirimi; JSON detail varsa gösterilir. Yanıtta toast tetikleyicisi varsa ikinci bildirim gösterilmez. data-silent-errors ile susturun.
422 + text/htmlHatalı form yeniden çizimi: hedefe yerleştirilir, hata bildirimi çıkmaz.
.btn.htmx-request · form.htmx-request .btn[type=submit]Meşgul buton (döner gösterge). Kapatmak: .btn--no-busy.
.htmx-indicator (--block)Yalnızca istek sürerken görünür.
.swap-fade (hedefte)Değiştirilen içerik yumuşakça belirir.
htmx:loadYeni gelen parçada sekmeler, sayaçlar, göreli zaman, dropzone otomatik başlatılır (App.init).

27 · Entegrasyon

JavaScript API

window.App — framework yok, her fonksiyon öğe yoksa sessizce geçer.

APIAçıklama
App.toast(msg, tone, {title, timeout})Bildirim gösterir; {el, close()} döner. timeout 0 → kalıcı.
App.confirm(msg, {title, ok, cancel, tone})Paylaşılan onay diyaloğu → Promise<boolean>.
App.copy(text)Panoya kopyalar (güvenli olmayan bağlamda yedek yöntem) → Promise<boolean>.
App.setTheme("light"|"dark"|"system") · App.toggleTheme() · App.getTheme()Tema; tercih localStorage["ceo-theme"]. Olay: theme:change.
App.relativeTime(root?)time[data-relative] etiketlerini yeniler (dakikada bir otomatik).
App.tabs.activate(root, key)Sekme değiştirir. Olaylar: tabs:change, tab-shown.
App.modal.open(id) · App.modal.close(id)Diyalog aç/kapat.
App.drawer.open() · close() · toggle()Mobil kenar çubuğu.
App.updateStep(stepper, key, {status, label, meta})Adım göstergesini canlı günceller.
App.setScore(ring, score) · App.scoreTone(score)Puan kadranını günceller / tonu hesaplar.
App.init(root?)Bir alt ağaçtaki bileşenleri başlatır (htmx:load'da otomatik).
App.formatNumber(n, opts?) · App.formatBytes(n)tr-TR sayı ve dosya boyutu biçimi.
App.icon(name, size)base.html'deki şablondan SVG ikon klonlar.

Bildirimsel kancalar

JS yazmadan kullanılan data-* öznitelikleri

Şimdi:
Kaynak
<textarea data-autosize data-autosize-max="560"></textarea>
<textarea data-counter="words" data-counter-target="#wc"></textarea> <span id="wc"></span>
<time data-relative datetime="2026-09-24T08:40:00+03:00">24.09.2026 08:40</time>
<button data-theme-toggle>Tema</button>  <button data-theme-set="system">Sistem</button>
<button data-drawer-open>Menü</button>
<button data-modal-open="share-modal">Aç</button>  <button data-modal-close>Kapat</button>
<div class="alert" data-dismissible> … <button data-dismiss>×</button></div>
<form data-shortcut-submit> … </form>          {# Ctrl/⌘ + Enter #}