## Styling und CSS-Integration
Keluna verwendet die System-WebView als Renderer. Deshalb sollte **CSS die primäre Styling-Technologie** sein. Der Kotlin-Code beschreibt Komponenten, Zustand und Verhalten; CSS beschreibt deren visuelle Darstellung.
Dabei soll CSS nicht als getrennte, globale „Web-Schicht“ behandelt werden. Komponenten sollen **nach Fachlichkeit bzw. Feature gruppiert** werden können, sodass Kotlin-Code und zugehöriges Styling direkt nebeneinander liegen.
### Grundprinzip
Eine Keluna-Komponente könnte beispielsweise so organisiert sein:
```text
projectbrowser/
├── ProjectBrowser.kt
├── ProjectBrowser.css
├── ProjectEntry.kt
└── ProjectEntry.css
```
Damit wird nicht nach Technologien wie `kotlin/`, `css/` und `javascript/` getrennt, sondern nach Komponenten bzw. Features.
### CSS-Klassen als Styling-Schnittstelle
Komponenten sollten ihren relevanten DOM-Elementen semantische CSS-Klassen geben können.
```kotlin
component("project-entry") {
div(classes = "project-entry") {
span(classes = "project-entry__name") {
text(project.name)
}
span(classes = "project-entry__status") {
text(project.status)
}
}
}
```
Dazu liegt direkt daneben beispielsweise:
```css
.project-entry {
display: flex;
align-items: center;
gap: 0.5rem;
}
.project-entry__name {
font-weight: 600;
}
.project-entry__status {
opacity: 0.7;
}
```
Die CSS-Klasse ist damit Teil der Styling-Schnittstelle einer Komponente.
### IDs nicht als primärer Styling-Mechanismus
Keluna benötigt intern eindeutige IDs, um DOM-Knoten mit Komponenten im Kotlin-Component-Tree zu verbinden.
Diese IDs sollten jedoch **keine öffentliche Styling-Schnittstelle** sein.
Intern könnte Keluna beispielsweise erzeugen:
```html
```
Diese ID wird automatisch generiert und ausschließlich für Runtime, Events und Synchronisation verwendet.
CSS sollte sich deshalb nicht darauf beziehen.
Falls der Entwickler selbst eine DOM-ID benötigt, etwa für Accessibility, Tests oder die Integration einer externen Web-Komponente, sollte Keluna auch explizite IDs erlauben. Deren Eindeutigkeit liegt dann jedoch in der Verantwortung der Anwendung.
### Komponentenlokales CSS
Keluna sollte CSS-Dateien direkt einer Komponente zuordnen können.
Konzeptionell:
```kotlin
class ProjectEntry : Component() {
override val stylesheet = stylesheet("ProjectEntry.css")
}
```
oder deklarativer:
```kotlin
component("project-entry") {
stylesheet("ProjectEntry.css")
// ...
}
```
Das Keluna-Gradle-Plugin sammelt diese Stylesheets beim Build und bettet sie zusammen mit den übrigen Web-Ressourcen in die Anwendung ein.
Der Entwickler muss dafür weder einen Webserver noch einen JavaScript-Bundler konfigurieren.
### CSS-Isolation
Lokales CSS sollte möglichst nicht versehentlich andere Komponenten beeinflussen.
Keluna könnte deshalb jedem Komponententyp automatisch einen Scope geben:
```html
...
```
Das Framework kann das lokale CSS beim Build entsprechend scopen.
Aus:
```css
.title {
font-weight: bold;
}
```
wird intern beispielsweise:
```css
[data-keluna-scope="project-entry"] .title {
font-weight: bold;
}
```
Damit kann eine Komponente einfache Klassennamen verwenden, ohne globale Namenskollisionen zu verursachen.
Die konkrete Implementierung des Scopings ist noch eine Designentscheidung. Denkbar sind:
- Selector-Rewriting beim Build
- modernes CSS Scoping
- Shadow DOM für dafür geeignete Komponenten
Für normale Keluna-Komponenten erscheint **Light DOM mit automatisch gescoptem CSS** zunächst attraktiver als Shadow DOM, da globales Theming und die Integration mit der restlichen Anwendung einfacher bleiben.
### Globales Styling und Themes
Neben komponentenlokalem CSS braucht eine Anwendung globale Designregeln.
Beispielsweise:
```text
src/
├── App.kt
├── theme.css
└── projectbrowser/
├── ProjectBrowser.kt
└── ProjectBrowser.css
```
`theme.css` könnte globale Design-Tokens über CSS Custom Properties definieren:
```css
:root {
--keluna-font-family: system-ui, sans-serif;
--keluna-font-size: 14px;
--keluna-spacing: 0.5rem;
--keluna-border-radius: 4px;
}
```
Eine Komponente verwendet diese Variablen:
```css
.project-entry {
gap: var(--keluna-spacing);
border-radius: var(--keluna-border-radius);
}
```
Keluna sollte nach Möglichkeit auch Systempräferenzen wie Dark Mode über normale CSS-Möglichkeiten nutzbar machen:
```css
@media (prefers-color-scheme: dark) {
:root {
/* anwendungsspezifische Anpassungen */
}
}
```
### Drei Styling-Ebenen
Keluna sollte Styling auf drei Ebenen ermöglichen:
1. **Framework Defaults**
Minimale Standarddarstellung der Keluna-Komponenten.
2. **Application Theme**
Globale Typografie, Farben, Abstände und Design-Tokens der Anwendung.
3. **Component Styles**
Direkt bei einer fachlichen Komponente liegendes CSS für deren konkrete Darstellung.
Konzeptionell:
```text
Keluna defaults
↓
Application theme
↓
Component-local CSS
```
### Inline-Styles aus Kotlin
Keluna sollte einfache dynamische Style-Eigenschaften auch direkt aus Kotlin setzen können.
```kotlin
progressBar {
style.width = "${progress}%"
}
```
Das ist sinnvoll für Werte, die tatsächlich aus dem Anwendungszustand entstehen.
Statisches Design sollte dagegen im CSS bleiben.
Die Trennung sollte lauten:
> **Kotlin beschreibt Zustand und Verhalten; CSS beschreibt Design.**
### Zustandsabhängiges Styling
Anwendungszustände sollten möglichst über Klassen oder Attribute an CSS übergeben werden.
```kotlin
projectEntry {
cssClass("selected", selected)
attribute("data-status", project.status)
}
```
Daraus könnte entstehen:
```html
```
Das CSS bleibt dann deklarativ:
```css
.project-entry.selected {
outline: 2px solid currentColor;
}
.project-entry[data-status="modified"] {
font-style: italic;
}
```
Dadurch muss Kotlin keine konkreten Farben, Abstände oder anderen Designentscheidungen kennen.
### Kein JavaScript im normalen Anwendungscode
Das Styling-Konzept erfordert **kein JavaScript vom Keluna-Anwendungsentwickler**.
Die kleine JavaScript-Schicht, die Keluna für DOM-Synchronisation und Event-Weiterleitung benötigt, ist Bestandteil des Frameworks.
Für normale Anwendungen besteht der Stack damit aus:
```text
Kotlin
+
CSS
↓
Keluna Runtime
↓
interne JavaScript-Bridge
↓
System-WebView
```
JavaScript bleibt ein möglicher Escape Hatch für spezielle Web-Komponenten oder externe Bibliotheken, ist aber **keine Voraussetzung für die Entwicklung einer Keluna-Anwendung**.
### Designziel
Ein Feature besteht idealerweise aus:
```text
ProjectBrowser.kt
ProjectBrowser.css
```
Kotlin enthält:
- Komponentenstruktur
- Zustand
- Events
- Anwendungslogik
CSS enthält:
- Layout
- Typografie
- Farben
- Animationen
- Responsive-Verhalten
- visuelle Zustände
Beides liegt direkt nebeneinander und bildet gemeinsam die Komponente.
Das erlaubt eine **fachliche Projektstruktur statt einer technologischen Trennung** und nutzt gleichzeitig die ausgereiften Styling-Fähigkeiten moderner WebViews.