Introduces a custom Aura theme variant (amber daylight / aurora cyan midnight, via -light/-dark token pairs) and a header toggle button to switch between them at runtime, using the Vaadin 25 ColorScheme API (Page#setColorScheme, @ColorScheme) rather than the older Lumo-only theme-attribute pattern. The choice is persisted in a cookie so it survives reloads and future sessions without any client-side script, and is restored flash-free on the next visit via an IndexHtmlRequestListener. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNL5syWGwM7KnFcuZeEKQ1
5.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
A Spring Boot + Vaadin Flow project (Vaadin 25, Spring Boot 4, Java 25). UI is built entirely in server-side Java — no HTML/JS/TypeScript to write for views. Data layer is Spring Data JPA over H2 (file/in-memory, dev only).
Commands
./mvnw spring-boot:run # run the app (dev), http://localhost:8080
./mvnw test # run all tests
./mvnw test -Dtest=TaskServiceTest # single test class
./mvnw test -Dtest=TaskServiceTest#tasks_are_validated_before_they_are_stored # single test method
./mvnw package # production build -> target/*.jar
java -jar target/*.jar # run the built jar
No system Maven is required; always use the ./mvnw wrapper. There is no separate lint command configured.
Live hotswap (edit Java, see changes without restart) requires launching via the Vaadin IDE plugin (IntelliJ/VS Code/Eclipse) instead of spring-boot:run — not available from the CLI.
Port defaults to 8080 (override via server.port in src/main/resources/application.properties or the PORT env var).
Architecture
Feature-package structure: code is organized by feature/domain under com.example.<feature>, not by technical layer. Each feature package is self-contained: entity, repository, service, and a ui sub-package for its views. com.example.examplefeature is the template feature to copy/replace — its package-info.java has a TODO Remove this package once you have added real features.
Package visibility convention: types are package-private by default (e.g. TaskRepository, TaskService, TaskListView are not public). Only export a type outside its feature package when another package actually needs it. Follow this pattern for new features.
Null-safety: packages are annotated @NullMarked (JSpecify) in their package-info.java, so all types are non-null by default; use @Nullable explicitly where null is allowed (e.g. Task.dueDate). Apply @NullMarked to any new feature package.
Layout/routing: MainLayout (com.example.base.ui, annotated @Layout) is the shared AppLayout shell (drawer nav + header/footer) applied automatically to routed views. Side-nav entries are auto-discovered via @Menu on @Route view classes (see TaskListView) — no manual registration needed. ViewTitle is a shared composite for the per-view title bar (drawer toggle + heading).
Data access pattern: Slice<T> (via findAllBy(Pageable)) is preferred over Page<T> for grid data providers, since Slice avoids the extra COUNT query — see the comment in TaskRepository. Vaadin Grid is wired to Spring Data via VaadinSpringDataHelpers.toSpringPageRequest(query) in an in-memory/lazy DataProvider callback (see TaskListView).
Entities: JPA entities use @GeneratedValue(strategy = GenerationType.SEQUENCE), expose validation in setters (throwing IllegalArgumentException) rather than via bean-validation annotations, and implement equals/hashCode based on ID only (with a fixed hashCode, per the pattern in Task).
Schema management: spring.jpa.hibernate.ddl-auto=update is used for local dev convenience only. This is explicitly not appropriate for production — the app is meant to move to Flyway (or similar) for real schema migrations before shipping.
Frontend theming: Application sets the app-shell config (@Push, Aura theme via @StyleSheet(Aura.STYLESHEET), plus custom styles.css/view-title.css/solstice.css under src/main/resources/META-INF/resources/). No separate frontend build step is needed for typical UI work since Flow generates the client bundle; the vaadin-maven-plugin build-frontend goal runs as part of the Maven build.
Custom theme ("Solstice") and dark/light mode: solstice.css overrides Aura's -light/-dark suffixed design tokens (e.g. --aura-accent-color-light/-dark, --aura-background-color-light/-dark) rather than the unsuffixed ones — those are read-only, computed via the native CSS light-dark() function from the pair. Everything else should keep consuming the unsuffixed tokens (--aura-accent-color, --vaadin-text-color, ...) so it follows whichever scheme is active automatically. Switching schemes at runtime uses the Vaadin 25 ColorScheme API (Application declares @ColorScheme(ColorScheme.Value.SYSTEM) as the default; Page#setColorScheme(...) changes it at runtime) — do not use the older Lumo-only setAttribute("theme", ...) pattern. The feature lives in com.example.base.ui: ThemePreference (session-scoped) holds the current choice and applies it; ThemeToggle is the header button; ThemeInitializer restores the choice from a cookie (ThemeCookie) on every request/UI-init, both for a flash-free reload and to seed a fresh session, since a plain @ColorScheme annotation can't read per-visitor state on its own.
Testing
Two distinct test styles are used:
- Service/integration tests (
TaskServiceTest):@SpringBootTest(webEnvironment = MOCK)+@Transactional(each test rolls back), asserting against the JPA layer directly. - UI/browserless tests (
TaskListViewTest): extendSpringBrowserlessTest(fromcom.vaadin:browserless-test-spring), which renders and interacts with actual Vaadin components server-side without a real browser. Usenavigate(ViewClass.class),test(component)for interactions/assertions, and$(ComponentClass.class)for component lookup queries. Views expose package-private fields (e.g.taskGrid,description,createBtn) specifically so tests in the same package can drive them directly.
MCP
.mcp.json configures a Vaadin docs MCP server (https://mcp.vaadin.com/docs) and a Playwright MCP server — both available for querying live Vaadin component/API docs and browser automation if needed.