diff --git a/README.md b/README.md
index 6b35c08..9511b0d 100644
--- a/README.md
+++ b/README.md
@@ -1,86 +1 @@
-# App README
-
-- [ ] TODO Replace or update this README with instructions relevant to your application
-
-## Project Structure
-
-The sources of your App have the following structure:
-
-```
-src
-├── main/frontend
-│ └── themes
-│ └── default
-│ ├── styles.css
-│ └── theme.json
-├── main/java
-│ └── [application package]
-│ ├── base
-│ │ └── ui
-│ │ ├── component
-│ │ │ └── ViewToolbar.java
-│ │ ├── MainErrorHandler.java
-│ │ └── MainLayout.java
-│ ├── examplefeature
-│ │ ├── ui
-│ │ │ └── TaskListView.java
-│ │ ├── Task.java
-│ │ ├── TaskRepository.java
-│ │ └── TaskService.java
-│ └── Application.java
-└── test/java
- └── [application package]
- └── examplefeature
- └── TaskServiceTest.java
-```
-
-The main entry point into the application is `Application.java`. This class contains the `main()` method that start up
-the Spring Boot application.
-
-The skeleton follows a *feature-based package structure*, organizing code by *functional units* rather than traditional
-architectural layers. It includes two feature packages: `base` and `examplefeature`.
-
-* The `base` package contains classes meant for reuse across different features, either through composition or
- inheritance. You can use them as-is, tweak them to your needs, or remove them.
-* The `examplefeature` package is an example feature package that demonstrates the structure. It represents a
- *self-contained unit of functionality*, including UI components, business logic, data access, and an integration test.
- Once you create your own features, *you'll remove this package*.
-
-The `src/main/frontend` directory contains an empty theme called `default`, based on the Lumo theme. It is activated in
-the `Application` class, using the `@Theme` annotation.
-
-## Starting in Development Mode
-
-To start the application in development mode, import it into your IDE and run the `Application` class.
-You can also start the application from the command line by running:
-
-```bash
-./mvnw
-```
-
-## Building for Production
-
-To build the application in production mode, run:
-
-```bash
-./mvnw -Pproduction package
-```
-
-To build a Docker image, run:
-
-```bash
-docker build -t my-application:latest .
-```
-
-If you use commercial components, pass the license key as a build secret:
-
-```bash
-docker build --secret id=proKey,src=$HOME/.vaadin/proKey .
-```
-
-## Getting Started
-
-The [Getting Started](https://vaadin.com/docs/latest/getting-started) guide will quickly familiarize you with your new
-App implementation. You'll learn how to set up your development environment, understand the project
-structure, and find resources to help you add muscles to your skeleton — transforming it into a fully-featured
-application.
+Demo application to show the contrast problem with warning messages using the Lumo theme of Vaadin.
diff --git a/mvnw b/mvnw
old mode 100755
new mode 100644
diff --git a/src/main/bundles/README.md b/src/main/bundles/README.md
new file mode 100644
index 0000000..c9737d1
--- /dev/null
+++ b/src/main/bundles/README.md
@@ -0,0 +1,32 @@
+This directory is automatically generated by Vaadin and contains the pre-compiled
+frontend files/resources for your project (frontend development bundle).
+
+It should be added to Version Control System and committed, so that other developers
+do not have to compile it again.
+
+Frontend development bundle is automatically updated when needed:
+- an npm/pnpm package is added with @NpmPackage or directly into package.json
+- CSS, JavaScript or TypeScript files are added with @CssImport, @JsModule or @JavaScript
+- Vaadin add-on with front-end customizations is added
+- Custom theme imports/assets added into 'theme.json' file
+- Exported web component is added.
+
+If your project development needs a hot deployment of the frontend changes,
+you can switch Flow to use Vite development server (default in Vaadin 23.3 and earlier versions):
+- set `vaadin.frontend.hotdeploy=true` in `application.properties`
+- configure `vaadin-maven-plugin`:
+```
+
+ true
+
+```
+- configure `jetty-maven-plugin`:
+```
+
+
+ true
+
+
+```
+
+Read more [about Vaadin development mode](https://vaadin.com/docs/next/flow/configuration/development-mode#precompiled-bundle).
\ No newline at end of file
diff --git a/src/main/bundles/dev.bundle b/src/main/bundles/dev.bundle
new file mode 100644
index 0000000..db786bd
Binary files /dev/null and b/src/main/bundles/dev.bundle differ
diff --git a/src/main/frontend/index.html b/src/main/frontend/index.html
new file mode 100644
index 0000000..eb0c53b
--- /dev/null
+++ b/src/main/frontend/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/frontend/themes/default/styles.css b/src/main/frontend/themes/default/styles.css
index 4eecf84..776ff8a 100644
--- a/src/main/frontend/themes/default/styles.css
+++ b/src/main/frontend/themes/default/styles.css
@@ -1 +1,16 @@
/* Add your styles here */
+
+.warning {
+ display: flex;
+ justify-content: center;
+ justify-items: center;
+ background-color: var(--lumo-warning-color);
+ color: var(--lumo-warning-text-color);
+ box-shadow: var(--lumo-box-shadow-s);
+ font-size: var(--lumo-font-size-xl);
+}
+
+h1 {
+ font-size: var(--lumo-font-size-l);
+ padding-top: var(--lumo-space-l);
+}
diff --git a/src/main/frontend/themes/default/theme.json b/src/main/frontend/themes/default/theme.json
index e36d300..2a27fca 100644
--- a/src/main/frontend/themes/default/theme.json
+++ b/src/main/frontend/themes/default/theme.json
@@ -1,9 +1,4 @@
{
- "lumoImports": [
- "typography",
- "color",
- "spacing",
- "badge",
- "utility"
- ]
-}
\ No newline at end of file
+ "lumoImports" : [ "typography", "color", "spacing", "badge", "utility" ],
+ "autoInjectComponents" : "false"
+}
diff --git a/src/main/java/com/example/base/ui/MainErrorHandler.java b/src/main/java/com/example/base/ui/MainErrorHandler.java
deleted file mode 100644
index 418324f..0000000
--- a/src/main/java/com/example/base/ui/MainErrorHandler.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.example.base.ui;
-
-import com.vaadin.flow.component.Component;
-import com.vaadin.flow.component.notification.Notification;
-import com.vaadin.flow.component.notification.NotificationVariant;
-import com.vaadin.flow.server.VaadinServiceInitListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-class MainErrorHandler {
-
- private static final Logger log = LoggerFactory.getLogger(MainErrorHandler.class);
-
- @Bean
- public VaadinServiceInitListener errorHandlerInitializer() {
- return (event) -> event.getSource().addSessionInitListener(
- sessionInitEvent -> sessionInitEvent.getSession().setErrorHandler(errorEvent -> {
- log.error("An unexpected error occurred", errorEvent.getThrowable());
- errorEvent.getComponent().flatMap(Component::getUI).ifPresent(ui -> {
- var notification = new Notification(
- "An unexpected error has occurred. Please try again later.");
- notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
- notification.setPosition(Notification.Position.TOP_CENTER);
- notification.setDuration(3000);
- ui.access(notification::open);
- });
- }));
- }
-}
diff --git a/src/main/java/com/example/base/ui/MainLayout.java b/src/main/java/com/example/base/ui/MainLayout.java
deleted file mode 100644
index 558849b..0000000
--- a/src/main/java/com/example/base/ui/MainLayout.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.example.base.ui;
-
-import com.vaadin.flow.component.applayout.AppLayout;
-import com.vaadin.flow.component.html.Div;
-import com.vaadin.flow.component.html.Span;
-import com.vaadin.flow.component.icon.Icon;
-import com.vaadin.flow.component.icon.VaadinIcon;
-import com.vaadin.flow.component.orderedlayout.Scroller;
-import com.vaadin.flow.component.sidenav.SideNav;
-import com.vaadin.flow.component.sidenav.SideNavItem;
-import com.vaadin.flow.router.Layout;
-import com.vaadin.flow.server.menu.MenuConfiguration;
-import com.vaadin.flow.server.menu.MenuEntry;
-
-import static com.vaadin.flow.theme.lumo.LumoUtility.*;
-
-@Layout
-public final class MainLayout extends AppLayout {
-
- MainLayout() {
- setPrimarySection(Section.DRAWER);
- addToDrawer(createHeader(), new Scroller(createSideNav()));
- }
-
- private Div createHeader() {
- // TODO Replace with real application logo and name
- var appLogo = VaadinIcon.CUBES.create();
- appLogo.addClassNames(TextColor.PRIMARY, IconSize.LARGE);
-
- var appName = new Span("App");
- appName.addClassNames(FontWeight.SEMIBOLD, FontSize.LARGE);
-
- var header = new Div(appLogo, appName);
- header.addClassNames(Display.FLEX, Padding.MEDIUM, Gap.MEDIUM, AlignItems.CENTER);
- return header;
- }
-
- private SideNav createSideNav() {
- var nav = new SideNav();
- nav.addClassNames(Margin.Horizontal.MEDIUM);
- MenuConfiguration.getMenuEntries().forEach(entry -> nav.addItem(createSideNavItem(entry)));
- return nav;
- }
-
- private SideNavItem createSideNavItem(MenuEntry menuEntry) {
- if (menuEntry.icon() != null) {
- return new SideNavItem(menuEntry.title(), menuEntry.path(), new Icon(menuEntry.icon()));
- } else {
- return new SideNavItem(menuEntry.title(), menuEntry.path());
- }
- }
-}
diff --git a/src/main/java/com/example/base/ui/component/ViewToolbar.java b/src/main/java/com/example/base/ui/component/ViewToolbar.java
deleted file mode 100644
index dace3ab..0000000
--- a/src/main/java/com/example/base/ui/component/ViewToolbar.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.example.base.ui.component;
-
-import com.vaadin.flow.component.Component;
-import com.vaadin.flow.component.Composite;
-import com.vaadin.flow.component.applayout.DrawerToggle;
-import com.vaadin.flow.component.html.Div;
-import com.vaadin.flow.component.html.H1;
-import com.vaadin.flow.component.html.Header;
-import com.vaadin.flow.theme.lumo.LumoUtility.*;
-import org.jspecify.annotations.NullMarked;
-import org.jspecify.annotations.Nullable;
-
-@NullMarked
-public final class ViewToolbar extends Composite {
-
- public ViewToolbar(@Nullable String viewTitle, Component... components) {
- addClassNames(Display.FLEX, FlexDirection.COLUMN, JustifyContent.BETWEEN, AlignItems.STRETCH, Gap.MEDIUM,
- FlexDirection.Breakpoint.Medium.ROW, AlignItems.Breakpoint.Medium.CENTER);
-
- var drawerToggle = new DrawerToggle();
- drawerToggle.addClassNames(Margin.NONE);
-
- var title = new H1(viewTitle);
- title.addClassNames(FontSize.XLARGE, Margin.NONE, FontWeight.LIGHT);
-
- var toggleAndTitle = new Div(drawerToggle, title);
- toggleAndTitle.addClassNames(Display.FLEX, AlignItems.CENTER);
- getContent().add(toggleAndTitle);
-
- if (components.length > 0) {
- var actions = new Div(components);
- actions.addClassNames(Display.FLEX, FlexDirection.COLUMN, JustifyContent.BETWEEN, Flex.GROW, Gap.SMALL,
- FlexDirection.Breakpoint.Medium.ROW);
- getContent().add(actions);
- }
- }
-
- public static Component group(Component... components) {
- var group = new Div(components);
- group.addClassNames(Display.FLEX, FlexDirection.COLUMN, AlignItems.STRETCH, Gap.SMALL,
- FlexDirection.Breakpoint.Medium.ROW, AlignItems.Breakpoint.Medium.CENTER);
- return group;
- }
-}
diff --git a/src/main/java/com/example/examplefeature/Task.java b/src/main/java/com/example/examplefeature/Task.java
deleted file mode 100644
index f412e08..0000000
--- a/src/main/java/com/example/examplefeature/Task.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.example.examplefeature;
-
-import jakarta.persistence.*;
-import org.jspecify.annotations.Nullable;
-
-import java.time.Instant;
-import java.time.LocalDate;
-
-@Entity
-@Table(name = "task")
-public class Task {
-
- public static final int DESCRIPTION_MAX_LENGTH = 300;
-
- @Id
- @GeneratedValue(strategy = GenerationType.SEQUENCE)
- @Column(name = "task_id")
- private Long id;
-
- @Column(name = "description", nullable = false, length = DESCRIPTION_MAX_LENGTH)
- private String description = "";
-
- @Column(name = "creation_date", nullable = false)
- private Instant creationDate;
-
- @Column(name = "due_date")
- @Nullable
- private LocalDate dueDate;
-
- protected Task() { // To keep Hibernate happy
- }
-
- public Task(String description, Instant creationDate) {
- setDescription(description);
- this.creationDate = creationDate;
- }
-
- public @Nullable Long getId() {
- return id;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- if (description.length() > DESCRIPTION_MAX_LENGTH) {
- throw new IllegalArgumentException("Description length exceeds " + DESCRIPTION_MAX_LENGTH);
- }
- this.description = description;
- }
-
- public Instant getCreationDate() {
- return creationDate;
- }
-
- public @Nullable LocalDate getDueDate() {
- return dueDate;
- }
-
- public void setDueDate(@Nullable LocalDate dueDate) {
- this.dueDate = dueDate;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || !getClass().isAssignableFrom(obj.getClass())) {
- return false;
- }
- if (obj == this) {
- return true;
- }
-
- Task other = (Task) obj;
- return getId() != null && getId().equals(other.getId());
- }
-
- @Override
- public int hashCode() {
- // Hashcode should never change during the lifetime of an object. Because of
- // this we can't use getId() to calculate the hashcode. Unless you have sets
- // with lots of entities in them, returning the same hashcode should not be a
- // problem.
- return getClass().hashCode();
- }
-}
diff --git a/src/main/java/com/example/examplefeature/TaskRepository.java b/src/main/java/com/example/examplefeature/TaskRepository.java
deleted file mode 100644
index a1b35d9..0000000
--- a/src/main/java/com/example/examplefeature/TaskRepository.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.example.examplefeature;
-
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Slice;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-
-interface TaskRepository extends JpaRepository, JpaSpecificationExecutor {
-
- // If you don't need a total row count, Slice is better than Page as it only performs a select query.
- // Page performs both a select and a count query.
- Slice findAllBy(Pageable pageable);
-}
diff --git a/src/main/java/com/example/examplefeature/TaskService.java b/src/main/java/com/example/examplefeature/TaskService.java
deleted file mode 100644
index 504ba3d..0000000
--- a/src/main/java/com/example/examplefeature/TaskService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.example.examplefeature;
-
-import org.jspecify.annotations.Nullable;
-import org.springframework.data.domain.Pageable;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.Instant;
-import java.time.LocalDate;
-import java.util.List;
-
-@Service
-public class TaskService {
-
- private final TaskRepository taskRepository;
-
- TaskService(TaskRepository taskRepository) {
- this.taskRepository = taskRepository;
- }
-
- @Transactional
- public void createTask(String description, @Nullable LocalDate dueDate) {
- if ("fail".equals(description)) {
- throw new RuntimeException("This is for testing the error handler");
- }
- var task = new Task(description, Instant.now());
- task.setDueDate(dueDate);
- taskRepository.saveAndFlush(task);
- }
-
- @Transactional(readOnly = true)
- public List list(Pageable pageable) {
- return taskRepository.findAllBy(pageable).toList();
- }
-
-}
diff --git a/src/main/java/com/example/examplefeature/package-info.java b/src/main/java/com/example/examplefeature/package-info.java
deleted file mode 100644
index 5764d7e..0000000
--- a/src/main/java/com/example/examplefeature/package-info.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * This is a feature package for the Task Management sample feature. Its purpose is to demonstrate how you typically
- * structure Vaadin business applications, and how the different building blocks interact.
- *
- * A feature package represents a self-contained unit of functionality, including UI components, business logic, and
- * data access. It could be a subdomain or bounded context (e.g., "Billing"), a specific use case (e.g., "User
- * Registration"), or even a complex UI view (e.g., "Dashboard").
- *
- *
- * If your application is very small, you may not need dedicated feature packages. In that case, move the subpackages
- * directly to the application package.
- *
- */
-@NullMarked
-package com.example.examplefeature;
-// TODO Remove this package once you have added real features
-
-import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/com/example/examplefeature/ui/TaskListView.java b/src/main/java/com/example/examplefeature/ui/TaskListView.java
deleted file mode 100644
index 6d3d31e..0000000
--- a/src/main/java/com/example/examplefeature/ui/TaskListView.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.example.examplefeature.ui;
-
-import com.example.base.ui.component.ViewToolbar;
-import com.example.examplefeature.Task;
-import com.example.examplefeature.TaskService;
-import com.vaadin.flow.component.button.Button;
-import com.vaadin.flow.component.button.ButtonVariant;
-import com.vaadin.flow.component.datepicker.DatePicker;
-import com.vaadin.flow.component.grid.Grid;
-import com.vaadin.flow.component.html.Main;
-import com.vaadin.flow.component.notification.Notification;
-import com.vaadin.flow.component.notification.NotificationVariant;
-import com.vaadin.flow.component.textfield.TextField;
-import com.vaadin.flow.router.Menu;
-import com.vaadin.flow.router.PageTitle;
-import com.vaadin.flow.router.Route;
-import com.vaadin.flow.theme.lumo.LumoUtility;
-
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
-import java.util.Optional;
-
-import static com.vaadin.flow.spring.data.VaadinSpringDataHelpers.toSpringPageRequest;
-
-@Route("")
-@PageTitle("Task List")
-@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Task List")
-class TaskListView extends Main {
-
- private final TaskService taskService;
-
- final TextField description;
- final DatePicker dueDate;
- final Button createBtn;
- final Grid taskGrid;
-
- TaskListView(TaskService taskService) {
- this.taskService = taskService;
-
- description = new TextField();
- description.setPlaceholder("What do you want to do?");
- description.setAriaLabel("Task description");
- description.setMaxLength(Task.DESCRIPTION_MAX_LENGTH);
- description.setMinWidth("20em");
-
- dueDate = new DatePicker();
- dueDate.setPlaceholder("Due date");
- dueDate.setAriaLabel("Due date");
-
- createBtn = new Button("Create", event -> createTask());
- createBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
-
- var dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(getLocale())
- .withZone(ZoneId.systemDefault());
- var dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(getLocale());
-
- taskGrid = new Grid<>();
- taskGrid.setItems(query -> taskService.list(toSpringPageRequest(query)).stream());
- taskGrid.addColumn(Task::getDescription).setHeader("Description");
- taskGrid.addColumn(task -> Optional.ofNullable(task.getDueDate()).map(dateFormatter::format).orElse("Never"))
- .setHeader("Due Date");
- taskGrid.addColumn(task -> dateTimeFormatter.format(task.getCreationDate())).setHeader("Creation Date");
- taskGrid.setSizeFull();
-
- setSizeFull();
- addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
- LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
-
- add(new ViewToolbar("Task List", ViewToolbar.group(description, dueDate, createBtn)));
- add(taskGrid);
- }
-
- private void createTask() {
- taskService.createTask(description.getValue(), dueDate.getValue());
- taskGrid.getDataProvider().refreshAll();
- description.clear();
- dueDate.clear();
- Notification.show("Task added", 3000, Notification.Position.BOTTOM_END)
- .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
- }
-
-}
diff --git a/src/main/java/com/example/views/TestView.java b/src/main/java/com/example/views/TestView.java
new file mode 100644
index 0000000..543f500
--- /dev/null
+++ b/src/main/java/com/example/views/TestView.java
@@ -0,0 +1,31 @@
+package com.example.views;
+
+import com.vaadin.flow.component.checkbox.Checkbox;
+import com.vaadin.flow.component.html.Div;
+import com.vaadin.flow.component.html.H1;
+import com.vaadin.flow.router.Route;
+import com.vaadin.flow.router.RouterLayout;
+
+@Route("")
+public class TestView extends Div implements RouterLayout {
+
+ public TestView() {
+
+ final var warning = new Div("This is a warning message.");
+ warning.setClassName("warning");
+ add(warning);
+
+ add(new H1("Lumo Theme Test"));
+
+ final var darkModeSwitcher = new Checkbox("Dark mode");
+ add(darkModeSwitcher);
+ darkModeSwitcher.addValueChangeListener(event -> {
+ if (event.getValue()) {
+ getElement().getThemeList().add("dark");
+ } else {
+ getElement().getThemeList().remove("dark");
+ }
+ });
+
+ }
+}