[FEATURE] Custom Theme #3
@@ -0,0 +1,39 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
./mvnw spring-boot:run # run app (default goal) — http://localhost:8080, ~30s first start
|
||||
./mvnw compile # compile only
|
||||
./mvnw package # production build → target/*.jar
|
||||
java -jar target/*.jar # run production jar
|
||||
```
|
||||
|
||||
No test sources exist yet (`src/test` is empty) — `spring-boot-starter-test` and `browserless-test-spring` are on the classpath but unused.
|
||||
|
||||
**Java 25 toolchain required** (`pom.xml` sets `java.version=25`). If the default JDK on PATH is older (check `java -version`), point `JAVA_HOME` at a Java 25 install for the Maven build, e.g.:
|
||||
```bash
|
||||
JAVA_HOME="/path/to/jdk-25" ./mvnw compile
|
||||
```
|
||||
|
||||
Port 8080 conflicts: a prior `spring-boot:run` left running in the background is the usual cause (Vaadin dev mode keeps a second process/thread alive under a different PID than the launching Maven process — killing the Maven process alone may not free the port). Find and stop the actual listener before restarting.
|
||||
|
||||
## Architecture
|
||||
|
||||
Vaadin Flow (server-side Java UI, no hand-written HTML/JS for views) on Spring Boot 4.1, using the **Aura** theme (not Lumo — no `@Theme` annotation; Aura is wired via `@StyleSheet(Aura.STYLESHEET)` in `Application.java`).
|
||||
|
||||
**Charts are ApexCharts, not Vaadin Charts.** The bridge lives in `components/`:
|
||||
- `ApexChart` (abstract, `@Tag("apex-chart")`) — owns the JS module (`frontend/components/apex-chart.ts`, a Lit element rendering into light DOM), serializes an options `Map` to JSON via Jackson and calls `renderChart` client-side, and exposes point-click events back to the server via `@ClientCallable`.
|
||||
- `AxisChart` (abstract) — builds `series`/`xaxis.categories` options for line/bar.
|
||||
- `LineChart`, `BarChart` extend `AxisChart`; `PieChart` extends `ApexChart` directly (`series`/`labels` instead of axis-based).
|
||||
- `DialectTheme` — single source of chart styling (categorical color palette, grid/legend/stroke option fragments). Every chart's `setData(...)` starts from `DialectTheme.baseOptions(chartType)` and merges in its data. Add new chart types here, not by duplicating option maps.
|
||||
|
||||
Since `apex-chart.ts` renders into light DOM (`createRenderRoot()` returns `this`), global CSS can reach into the chart markup — but series/legend/grid colors are driven entirely by the options JSON, not CSS, because ApexCharts renders its own SVG/canvas.
|
||||
|
||||
**Views** (`views/`): `MainLayout` (`@Layout`, applies to all routes) is the `AppLayout` shell — navbar + `SideNav` drawer. `DashboardView` (`@Route("")`) is the only route; it wraps each chart in a card via a local `card(title, chart)` helper rather than adding charts directly.
|
||||
|
||||
**Styling**: `src/main/resources/META-INF/resources/styles.css` is the one project-level stylesheet (loaded via `@StyleSheet("styles.css")` in `Application.java`). It defines `--dialect-*` design tokens (Dialect design system: primary orange `#E86C00`, cool-gray background, card radius/shadow) and aliases them onto Aura's own CSS custom properties (`--aura-accent-color-*`, `--aura-background-color-*`, `--aura-orange`, `--aura-yellow`) rather than fighting the theme. Aura tokens use OKLCH + relative-color syntax and accept plain hex overrides. When restyling, prefer extending this alias layer over hardcoding new colors in components.
|
||||
|
||||
UI copy/data (chart labels, notifications) is in German.
|
||||
@@ -10,11 +10,13 @@ public abstract class AxisChart extends ApexChart {
|
||||
}
|
||||
|
||||
public void setData(String seriesName, List<Double> values, List<String> categories) {
|
||||
Map<String, Object> options = Map.of(
|
||||
"chart", Map.of("type", getChartType(), "height", "100%"),
|
||||
"series", List.of(Map.of("name", seriesName, "data", values)),
|
||||
"xaxis", Map.of("categories", categories)
|
||||
);
|
||||
Map<String, Object> options = DialectTheme.baseOptions(getChartType());
|
||||
options.put("series", List.of(Map.of("name", seriesName, "data", values)));
|
||||
options.put("xaxis", Map.of(
|
||||
"categories", categories,
|
||||
"axisBorder", Map.of("show", false),
|
||||
"axisTicks", Map.of("show", false)
|
||||
));
|
||||
|
||||
sendOptions(options);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.example.components;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Shared ApexCharts styling for the Dialect design system
|
||||
* (https://www.figma.com/design/6DRThj0vmRgfjMIdsmkaIy).
|
||||
*/
|
||||
public final class DialectTheme {
|
||||
|
||||
/** Categorical palette, primary orange first. */
|
||||
public static final List<String> COLORS = List.of(
|
||||
"#E86C00", // primary orange
|
||||
"#4C9BE8", // blue
|
||||
"#2FB7A6", // teal
|
||||
"#F6B93B", // gold
|
||||
"#8E7CD6", // purple
|
||||
"#E5544B", // red
|
||||
"#3F51B5" // indigo
|
||||
);
|
||||
|
||||
private static final String FONT_FAMILY =
|
||||
"'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
||||
|
||||
/**
|
||||
* Mirrors {@code --dialect-border} in styles.css. Charts render via JS/SVG,
|
||||
* not CSS, so this can't read the custom property directly — keep both in sync
|
||||
* by hand if the token changes.
|
||||
*/
|
||||
private static final String BORDER_COLOR = "#E6EAF0";
|
||||
|
||||
/** Mirrors {@code --dialect-ink} in styles.css. See {@link #BORDER_COLOR} note. */
|
||||
private static final String INK_COLOR = "#14171F";
|
||||
|
||||
private DialectTheme() {
|
||||
}
|
||||
|
||||
/** Common option fragments merged into every chart before series/labels are added. */
|
||||
public static Map<String, Object> baseOptions(String chartType) {
|
||||
Map<String, Object> options = new LinkedHashMap<>();
|
||||
|
||||
Map<String, Object> chart = new LinkedHashMap<>();
|
||||
chart.put("type", chartType);
|
||||
chart.put("height", "100%");
|
||||
chart.put("fontFamily", FONT_FAMILY);
|
||||
chart.put("toolbar", Map.of("show", false));
|
||||
options.put("chart", chart);
|
||||
|
||||
options.put("colors", COLORS);
|
||||
|
||||
options.put("grid", Map.of(
|
||||
"borderColor", BORDER_COLOR,
|
||||
"strokeDashArray", 4
|
||||
));
|
||||
|
||||
options.put("legend", Map.of(
|
||||
"position", "bottom",
|
||||
"labels", Map.of("colors", INK_COLOR)
|
||||
));
|
||||
|
||||
options.put("dataLabels", Map.of("enabled", false));
|
||||
|
||||
options.put("tooltip", Map.of("theme", "light"));
|
||||
|
||||
if ("line".equals(chartType)) {
|
||||
options.put("stroke", Map.of("curve", "smooth", "width", 3));
|
||||
} else if ("bar".equals(chartType)) {
|
||||
options.put("stroke", Map.of("width", 0));
|
||||
options.put("plotOptions", Map.of(
|
||||
"bar", Map.of(
|
||||
"borderRadius", 6,
|
||||
"borderRadiusApplication", "end",
|
||||
"columnWidth", "55%"
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,9 @@ public class PieChart extends ApexChart {
|
||||
}
|
||||
|
||||
public void setData(List<Double> values, List<String> labels) {
|
||||
Map<String, Object> options = Map.of(
|
||||
"chart", Map.of("type", "pie", "height", "100%"),
|
||||
"series", values,
|
||||
"labels", labels
|
||||
);
|
||||
Map<String, Object> options = DialectTheme.baseOptions("pie");
|
||||
options.put("series", values);
|
||||
options.put("labels", labels);
|
||||
|
||||
sendOptions(options);
|
||||
}
|
||||
|
||||
@@ -3,16 +3,22 @@ package com.example.views;
|
||||
import com.example.components.BarChart;
|
||||
import com.example.components.LineChart;
|
||||
import com.example.components.PieChart;
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Route("")
|
||||
@PageTitle("Dashboard")
|
||||
public class DashboardView extends VerticalLayout {
|
||||
|
||||
public DashboardView() {
|
||||
addClassName("dialect-content");
|
||||
|
||||
LineChart lineChart = new LineChart();
|
||||
lineChart.setWidthFull();
|
||||
lineChart.setHeight("400px");
|
||||
@@ -45,6 +51,20 @@ public class DashboardView extends VerticalLayout {
|
||||
Notification.show("Slice %d".formatted(e.getDataPointIndex()));
|
||||
});
|
||||
|
||||
add(lineChart, barChart, pieChart);
|
||||
add(card("Umsatz-Entwicklung", lineChart),
|
||||
card("Umsatz nach Monat", barChart),
|
||||
card("Umsatz nach Region", pieChart));
|
||||
}
|
||||
|
||||
private Component card(String title, Component chart) {
|
||||
H3 heading = new H3(title);
|
||||
heading.addClassName("dialect-card__title");
|
||||
|
||||
VerticalLayout card = new VerticalLayout(heading, chart);
|
||||
card.addClassName("dialect-card");
|
||||
card.setWidthFull();
|
||||
card.setPadding(false);
|
||||
card.setSpacing(false);
|
||||
return card;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package com.example.views;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
import com.vaadin.flow.component.button.Button;
|
||||
import com.vaadin.flow.component.button.ButtonVariant;
|
||||
import com.vaadin.flow.component.checkbox.Checkbox;
|
||||
import com.vaadin.flow.component.checkbox.CheckboxGroup;
|
||||
import com.vaadin.flow.component.combobox.ComboBox;
|
||||
import com.vaadin.flow.component.datepicker.DatePicker;
|
||||
import com.vaadin.flow.component.formlayout.FormLayout;
|
||||
import com.vaadin.flow.component.html.H3;
|
||||
import com.vaadin.flow.component.notification.Notification;
|
||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||
import com.vaadin.flow.component.radiobutton.RadioButtonGroup;
|
||||
import com.vaadin.flow.component.select.Select;
|
||||
import com.vaadin.flow.component.textfield.BigDecimalField;
|
||||
import com.vaadin.flow.component.textfield.EmailField;
|
||||
import com.vaadin.flow.component.textfield.IntegerField;
|
||||
import com.vaadin.flow.component.textfield.PasswordField;
|
||||
import com.vaadin.flow.component.textfield.TextArea;
|
||||
import com.vaadin.flow.component.textfield.TextField;
|
||||
import com.vaadin.flow.component.timepicker.TimePicker;
|
||||
import com.vaadin.flow.data.binder.Binder;
|
||||
import com.vaadin.flow.data.validator.EmailValidator;
|
||||
import com.vaadin.flow.data.validator.IntegerRangeValidator;
|
||||
import com.vaadin.flow.router.PageTitle;
|
||||
import com.vaadin.flow.router.Route;
|
||||
|
||||
|
||||
/**
|
||||
* Example view demonstrating a broad set of Vaadin form controls bound
|
||||
* to {@link Registration} via {@link Binder}.
|
||||
*/
|
||||
@Route("formular")
|
||||
@PageTitle("Formular")
|
||||
public class FormView extends VerticalLayout {
|
||||
|
||||
private final Binder<Registration> binder = new Binder<>(Registration.class);
|
||||
|
||||
public FormView() {
|
||||
addClassName("dialect-content");
|
||||
add(card("Registrierung", buildForm()));
|
||||
}
|
||||
|
||||
private Component buildForm() {
|
||||
TextField vorname = new TextField("Vorname");
|
||||
TextField nachname = new TextField("Nachname");
|
||||
EmailField email = new EmailField("E-Mail");
|
||||
PasswordField passwort = new PasswordField("Passwort");
|
||||
IntegerField alter = new IntegerField("Alter");
|
||||
BigDecimalField gehalt = new BigDecimalField("Gehalt (€)");
|
||||
DatePicker geburtsdatum = new DatePicker("Geburtsdatum");
|
||||
TimePicker uhrzeit = new TimePicker("Bevorzugte Uhrzeit");
|
||||
|
||||
ComboBox<String> land = new ComboBox<>("Land");
|
||||
land.setItems("Deutschland", "Österreich", "Schweiz", "Frankreich", "Niederlande");
|
||||
|
||||
Select<String> abteilung = new Select<>();
|
||||
abteilung.setLabel("Abteilung");
|
||||
abteilung.setItems("Vertrieb", "Marketing", "Entwicklung", "Support", "Geschäftsführung");
|
||||
|
||||
CheckboxGroup<String> interessen = new CheckboxGroup<>();
|
||||
interessen.setLabel("Interessen");
|
||||
interessen.setItems("Newsletter-Themen", "Webinare", "Produkt-Updates", "Events");
|
||||
|
||||
RadioButtonGroup<String> geschlecht = new RadioButtonGroup<>();
|
||||
geschlecht.setLabel("Anrede");
|
||||
geschlecht.setItems("Frau", "Herr", "Divers", "Keine Angabe");
|
||||
|
||||
Checkbox newsletter = new Checkbox("Newsletter abonnieren");
|
||||
|
||||
TextArea bemerkung = new TextArea("Bemerkung");
|
||||
bemerkung.setMaxLength(500);
|
||||
|
||||
binder.forField(vorname)
|
||||
.asRequired("Vorname wird benötigt")
|
||||
.bind(Registration::getVorname, Registration::setVorname);
|
||||
binder.forField(nachname)
|
||||
.asRequired("Nachname wird benötigt")
|
||||
.bind(Registration::getNachname, Registration::setNachname);
|
||||
binder.forField(email)
|
||||
.asRequired("E-Mail wird benötigt")
|
||||
.withValidator(new EmailValidator("Ungültige E-Mail-Adresse"))
|
||||
.bind(Registration::getEmail, Registration::setEmail);
|
||||
binder.forField(passwort)
|
||||
.bind(Registration::getPasswort, Registration::setPasswort);
|
||||
binder.forField(alter)
|
||||
.withValidator(new IntegerRangeValidator("Alter muss zwischen 0 und 120 liegen", 0, 120))
|
||||
.bind(Registration::getAlter, Registration::setAlter);
|
||||
binder.forField(gehalt)
|
||||
.bind(Registration::getGehalt, Registration::setGehalt);
|
||||
binder.forField(geburtsdatum)
|
||||
.bind(Registration::getGeburtsdatum, Registration::setGeburtsdatum);
|
||||
binder.forField(uhrzeit)
|
||||
.bind(Registration::getUhrzeit, Registration::setUhrzeit);
|
||||
binder.forField(land)
|
||||
.bind(Registration::getLand, Registration::setLand);
|
||||
binder.forField(abteilung)
|
||||
.bind(Registration::getAbteilung, Registration::setAbteilung);
|
||||
binder.forField(interessen)
|
||||
.bind(Registration::getInteressen, Registration::setInteressen);
|
||||
binder.forField(geschlecht)
|
||||
.bind(Registration::getGeschlecht, Registration::setGeschlecht);
|
||||
binder.forField(newsletter)
|
||||
.bind(Registration::isNewsletter, Registration::setNewsletter);
|
||||
binder.forField(bemerkung)
|
||||
.bind(Registration::getBemerkung, Registration::setBemerkung);
|
||||
|
||||
Registration registration = new Registration();
|
||||
binder.readBean(registration);
|
||||
|
||||
FormLayout formLayout = new FormLayout();
|
||||
formLayout.setResponsiveSteps(
|
||||
new FormLayout.ResponsiveStep("0", 1),
|
||||
new FormLayout.ResponsiveStep("500px", 2));
|
||||
formLayout.add(vorname, nachname, email, passwort, alter, gehalt,
|
||||
geburtsdatum, uhrzeit, land, abteilung);
|
||||
formLayout.setColspan(interessen, 2);
|
||||
formLayout.setColspan(geschlecht, 2);
|
||||
formLayout.setColspan(newsletter, 2);
|
||||
formLayout.setColspan(bemerkung, 2);
|
||||
formLayout.add(interessen, geschlecht, newsletter, bemerkung);
|
||||
|
||||
Button speichern = new Button("Speichern", e -> {
|
||||
Registration ziel = new Registration();
|
||||
if (binder.writeBeanIfValid(ziel)) {
|
||||
Notification erfolg = Notification.show(
|
||||
"Gespeichert: %s %s".formatted(ziel.getVorname(), ziel.getNachname()));
|
||||
erfolg.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
|
||||
} else {
|
||||
Notification fehler = Notification.show("Bitte Eingaben prüfen");
|
||||
fehler.addThemeVariants(NotificationVariant.LUMO_ERROR);
|
||||
}
|
||||
});
|
||||
speichern.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||
|
||||
Button zuruecksetzen = new Button("Zurücksetzen",
|
||||
e -> binder.readBean(new Registration()));
|
||||
|
||||
HorizontalLayout buttons = new HorizontalLayout(speichern, zuruecksetzen);
|
||||
|
||||
VerticalLayout wrapper = new VerticalLayout(formLayout, buttons);
|
||||
wrapper.setPadding(false);
|
||||
wrapper.setSpacing(true);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private Component card(String title, Component body) {
|
||||
H3 heading = new H3(title);
|
||||
heading.addClassName("dialect-card__title");
|
||||
|
||||
VerticalLayout card = new VerticalLayout(heading, body);
|
||||
card.addClassName("dialect-card");
|
||||
card.setWidthFull();
|
||||
card.setPadding(false);
|
||||
card.setSpacing(false);
|
||||
return card;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.example.views;
|
||||
|
||||
import com.vaadin.flow.component.applayout.AppLayout;
|
||||
import com.vaadin.flow.component.applayout.DrawerToggle;
|
||||
import com.vaadin.flow.component.html.Span;
|
||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||
import com.vaadin.flow.component.sidenav.SideNav;
|
||||
import com.vaadin.flow.component.sidenav.SideNavItem;
|
||||
import com.vaadin.flow.router.Layout;
|
||||
|
||||
@Layout
|
||||
public class MainLayout extends AppLayout {
|
||||
|
||||
public MainLayout() {
|
||||
DrawerToggle toggle = new DrawerToggle();
|
||||
|
||||
Span title = new Span("Chart App");
|
||||
title.addClassName("dialect-title");
|
||||
|
||||
addToNavbar(toggle, title);
|
||||
|
||||
SideNav nav = new SideNav();
|
||||
nav.addItem(new SideNavItem("Dashboard", DashboardView.class,
|
||||
VaadinIcon.DASHBOARD.create()));
|
||||
nav.addItem(new SideNavItem("Formular", FormView.class,
|
||||
VaadinIcon.FORM.create()));
|
||||
|
||||
addToDrawer(nav);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.example.views;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Plain POJO backing the example form on {@link FormView}. Bound via
|
||||
* {@link com.vaadin.flow.data.binder.Binder} — one field per showcased
|
||||
* Vaadin control type.
|
||||
*/
|
||||
public class Registration {
|
||||
|
||||
private String vorname;
|
||||
private String nachname;
|
||||
private String email;
|
||||
private String passwort;
|
||||
private Integer alter;
|
||||
private BigDecimal gehalt;
|
||||
private LocalDate geburtsdatum;
|
||||
private LocalTime uhrzeit;
|
||||
private String land;
|
||||
private String abteilung;
|
||||
private Set<String> interessen = new HashSet<>();
|
||||
private String geschlecht;
|
||||
private boolean newsletter;
|
||||
private String bemerkung;
|
||||
|
||||
public String getVorname() {
|
||||
return vorname;
|
||||
}
|
||||
|
||||
public void setVorname(String vorname) {
|
||||
this.vorname = vorname;
|
||||
}
|
||||
|
||||
public String getNachname() {
|
||||
return nachname;
|
||||
}
|
||||
|
||||
public void setNachname(String nachname) {
|
||||
this.nachname = nachname;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPasswort() {
|
||||
return passwort;
|
||||
}
|
||||
|
||||
public void setPasswort(String passwort) {
|
||||
this.passwort = passwort;
|
||||
}
|
||||
|
||||
public Integer getAlter() {
|
||||
return alter;
|
||||
}
|
||||
|
||||
public void setAlter(Integer alter) {
|
||||
this.alter = alter;
|
||||
}
|
||||
|
||||
public BigDecimal getGehalt() {
|
||||
return gehalt;
|
||||
}
|
||||
|
||||
public void setGehalt(BigDecimal gehalt) {
|
||||
this.gehalt = gehalt;
|
||||
}
|
||||
|
||||
public LocalDate getGeburtsdatum() {
|
||||
return geburtsdatum;
|
||||
}
|
||||
|
||||
public void setGeburtsdatum(LocalDate geburtsdatum) {
|
||||
this.geburtsdatum = geburtsdatum;
|
||||
}
|
||||
|
||||
public LocalTime getUhrzeit() {
|
||||
return uhrzeit;
|
||||
}
|
||||
|
||||
public void setUhrzeit(LocalTime uhrzeit) {
|
||||
this.uhrzeit = uhrzeit;
|
||||
}
|
||||
|
||||
public String getLand() {
|
||||
return land;
|
||||
}
|
||||
|
||||
public void setLand(String land) {
|
||||
this.land = land;
|
||||
}
|
||||
|
||||
public String getAbteilung() {
|
||||
return abteilung;
|
||||
}
|
||||
|
||||
public void setAbteilung(String abteilung) {
|
||||
this.abteilung = abteilung;
|
||||
}
|
||||
|
||||
public Set<String> getInteressen() {
|
||||
return interessen;
|
||||
}
|
||||
|
||||
public void setInteressen(Set<String> interessen) {
|
||||
this.interessen = interessen;
|
||||
}
|
||||
|
||||
public String getGeschlecht() {
|
||||
return geschlecht;
|
||||
}
|
||||
|
||||
public void setGeschlecht(String geschlecht) {
|
||||
this.geschlecht = geschlecht;
|
||||
}
|
||||
|
||||
public boolean isNewsletter() {
|
||||
return newsletter;
|
||||
}
|
||||
|
||||
public void setNewsletter(boolean newsletter) {
|
||||
this.newsletter = newsletter;
|
||||
}
|
||||
|
||||
public String getBemerkung() {
|
||||
return bemerkung;
|
||||
}
|
||||
|
||||
public void setBemerkung(String bemerkung) {
|
||||
this.bemerkung = bemerkung;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,43 @@
|
||||
/* Add your styles here */
|
||||
/* Dialect design system tokens (https://www.figma.com/design/6DRThj0vmRgfjMIdsmkaIy) */
|
||||
:root {
|
||||
--dialect-primary: #e86c00;
|
||||
--dialect-secondary: #f6b93b;
|
||||
--dialect-bg: #f4f6f9;
|
||||
--dialect-surface: #ffffff;
|
||||
--dialect-ink: #14171f;
|
||||
--dialect-border: #e6eaf0;
|
||||
--dialect-radius: 14px;
|
||||
--dialect-shadow: 0 6px 20px rgba(20, 23, 31, 0.08), 0 1px 2px rgba(20, 23, 31, 0.06);
|
||||
|
||||
/* Alias onto Aura's theming hooks */
|
||||
--aura-accent-color-light: var(--dialect-primary);
|
||||
--aura-accent-color-dark: var(--dialect-primary);
|
||||
--aura-background-color-light: var(--dialect-bg);
|
||||
--aura-orange: var(--dialect-primary);
|
||||
--aura-yellow: var(--dialect-secondary);
|
||||
}
|
||||
|
||||
.dialect-title {
|
||||
font-weight: 600;
|
||||
color: var(--dialect-ink);
|
||||
}
|
||||
|
||||
.dialect-content {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: var(--lumo-space-l, 24px);
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.dialect-card {
|
||||
background: var(--dialect-surface);
|
||||
border-radius: var(--dialect-radius);
|
||||
box-shadow: var(--dialect-shadow);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dialect-card__title {
|
||||
font-weight: 600;
|
||||
color: var(--dialect-ink);
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user