310 lines
7.3 KiB
Markdown
310 lines
7.3 KiB
Markdown
## 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
|
|
<button data-keluna-id="k184">Open</button>
|
|
```
|
|
|
|
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
|
|
<div data-keluna-scope="project-entry">
|
|
...
|
|
</div>
|
|
```
|
|
|
|
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
|
|
<div class="project-entry selected" data-status="modified">
|
|
```
|
|
|
|
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.
|