Documentation

Documentation versions (currently viewingVaadin 24)

Site Content

The page hierarchy of the documentation website is defined by the folder structure of files written using the AsciiDoc markup language. Learn how to write these documentation pages.

Content Folder

Documentation pages are located in the articles folder by default. This content root path can be configured with the ARTICLES_PATH property.

Project Structure

The most important files in the documentation project are shown here with their directory tree placement:

my-docs               (1)
├── articles/         (2)
├── dspublisher/
│   └──docs-theme
│      └──global.css  (3)
├── frontend/         (4)
├── src/main/java/    (5)
└── pom.xml           (6)
  1. The root folder name is the short name provided to the initialization script.

  2. Page content in AsciiDoc files.

  3. Website style customizations.

  4. TypeScript component examples.

  5. Java component examples.

  6. Maven Project Object Model (POM) for theme and component dependencies.

Page Hierarchy

The file and folder hierarchy defines the website URL and navigation structure directly. Each AsciiDoc file (.adoc or .asciidoc) inside the content folder corresponds to a page on the website.

For example:

Path URL Navigation hierarchy

articles/components/button/style.adoc

https://localhost:8000/components/button/style

Components  Button → Style

Each folder should have an index.adoc file, which defines the title and description of that section, and perhaps some content for the index page. An empty index page displays an automatically generated section outline (i.e., a listing of the sub-pages and sub-sections in that section).

Page Metadata (Front Matter)

Each AsciiDoc file starts with a YAML formatted metadata block called the "front matter", defined between lines with three dashes. The following front matter attributes can be defined in this block. Only the title attribute is required.

title string

Required. The page’s title as shown in the navigation menus/trees, breadcrumbs, search results, and social media share previews. This title can be different from the main page heading (i.e., the largest heading on the page), which is defined using AsciiDoc. For tabbed pages, this title attribute defines the main page heading, as well.

description string

Page description. Shown under the main page heading and in section outlines. Also used in social media share previews.

layout string

Only useful for index.adoc pages. Set to tabbed-page to display the pages in this section as tabs on the index page instead of as child items in the navigation tree.

tab-title string

The title displayed for this page when it is displayed as a tab. Useful if you want to show a shorter title in the tab and a longer one in the breadcrumb or search results.

page-links list of string

Links which are displayed in the page header. Can be used for linking, for example, to the component repository, Figma file, or other resources. The links are defined using AsciiDoc syntax, and they can also contain simple formatting.

order number

Overrides the placement of the page in the navigation tree among the sibling pages in the same section. By default, pages are ordered alphabetically based on the title. Pages without an order are placed after ordered pages. It’s a good practice to make them spaced by 10 or 100, so you can add pages between existing ones without changing the number in other pages.

banner string

Define a banner which is shown directly under the site header. A banner defined in the root index.adoc file is shown on all documentation pages, but it can be overridden with a page level banner.

banner-style string

Define the banner style. Possible values are tip, important, warning, and caution.

banner-id string

Define the banner ID. If defined, the user can dismiss the banner permanently on their device. A banner without an ID is displayed again after a page reload.

section-nav string

Allows you to control the page navigation item. Possible values are:

  • expanded: Expand this section in the navigation tree by default. Only applicable for index.adoc pages, which are not using tabbed page layout.

  • hidden: Hide this page from the navigation tree, section outlines, and page navigation at the bottom of each page. If you set this on an index.adoc page, all sub-pages are also hidden from the navigation tree, but they are not hidden from section outlines or page navigation.

  • A custom value, or a combination of values (e.g., expanded custom-value). The complete value is used as a class name on the corresponding HTML element in the navigation tree and section outlines, which allows you to add custom CSS styles for that item.

url string

Define an external URL to link to. This causes the page to not be created on the documentation website (i.e., all content in that file is ignored). The navigation item is instead a link to this external URL.

The following is a comprehensive example of the page metadata attributes:

---
title: Accordion
description: Accordion is a vertically stacked set of expandable panels. It reduces clutter and helps maintain the user's focus by showing only the relevant content at any time.
layout: tabbed-page
tab-title: Usage
page-links:
  - https://github.com/vaadin/vaadin-flow-components/releases/tag/24.3.7[Flow 24.3.7]
  - https://github.com/vaadin/web-components/tree/v24.3.8/packages/accordion[Web Component 24.3.8]
order: 10
banner: See the new https://example.org/blog[blog post]!
banner-style: tip
banner-id: my-new-blog-post
section-nav: expanded beta-component
---

// This is the main page heading...
= Accordion

// ...and the rest of the page content goes here

Page Partials

Files whose names start with an underscore (e.g., _shared.adoc) and which don’t define any page metadata, don’t appear as pages in the website. Those files can be used to share content using AsciiDoc’s include directive.

Main Landing Page & Site Name

The main landing page of the documentation website is defined with the articles/index.adoc file. The title front matter attribute defines the visible name in the website header.

AsciiDoc Syntax

The content of each page is written in the AsciiDoc syntax, which has some similarities with Markdown. See the AsciiDoc Syntax Quick Reference for a comprehensive list of AsciiDoc features.

Section Outline

Using the section outline macro, section index pages (i.e., all index.adoc files) can include an automatically generated outline of its sub-pages and sub-sections. The outline lists each sub-page or sub-section name and description.

For example:

---
title: Section Name
description: This section contains multiple sub-pages and sub-sections.
---

= Section Name

Here's a brief overview of the contents in this section.
You can learn more from the following pages:

// tag::snippet[]
section_outline::[]
// end::snippet[]

This macro is not part of the standard AsciiDoc syntax, but an extension Design System Publisher provides.

Code Examples

To display monospace text within regular text, wrap words in backticks (i.e., the ` character). For example:

A paragraph with monospace text.

A paragraph with `monospace` text.

Use source code blocks to display code or command-line examples. Syntax highlighting for various languages is supported. The most commonly used ones are java, typescript, html, and css. For example:

Button button = new Button("My Button");
// tag::snippet[]
[source,java]
// end::snippet[]
----
Button button = new Button("My Button");
----

Source Code Block Title

By default, the source code language is used as the title of the block. You can define a custom title, as well. For example, you could define a file name to illustrate the location of the code like so:

Button button = new Button("My Button");
// tag::snippet[]
.MyButtonExample.java
// end::snippet[]
[source,java]
----
Button button = new Button("My Button");
----

Combine Related Code Examples Together

You can combine multiple code examples together if you specify them inside an .example style open block. This is a good practice for showing a UI source code example, while having a data model or resource files available.

List<Person> people = DataService.getPeople(); // See Person.java
VirtualList<Person> list = new VirtualList<>();
list.setItems(people);
[.example]
--
.VirtualListExample.java
[source,java]
----
List<Person> people = DataService.getPeople(); // See Person.java
VirtualList<Person> list = new VirtualList<>();
list.setItems(people);
----

.Person.java
[source,java]
----
public class Person {
  // The source of the Person object
}
----

.Address.java
[source,java]
----
public class Address {
  // The source of the Address object
}
----
--

Code Examples from Source Files

You can include code examples from any file in the documentation project, by using the AsciiDoc include directive — primarily from files in the frontend and src/main/java source code folders, but practically from any folder in the project. The benefit of including them from the source code folders is that those files are compiled during the build. This ensures that any compilation errors in the code examples are detected early.

Use the AsciiDoc include directive to include them in code blocks as follows:

[source,java]
----
include::{root}/src/main/java/path/to/MyExample.java[]
----

Use the {root} attribute reference at the start of the path, if you want to use an absolute reference — starting from the project root. Otherwise the path is relative to the AsciiDoc file where the include directive is used.

By default, the name of the file included is used as the title for the source code block. You can customize the title if needed.

Include Parameters

You can pass various parameters to the include directive, between the trailing square brackets, to affect how the code example is included.

render

Renders a interactive example, as described in Rendered UI Examples.

tags=<name>

Specifies tags for extracting a source code snippets. See Snippets for more information.

indent=<spaces>

Specifies the indentation of the code inside the listing. Without the parameter, the indentation in the source listing is used. You should use indent=0.

group=<name>

Groups the file under a group tab. You can use this for language groups, such as group=Java and group=TypeScript in corresponding include statements. You shouldn’t use it if you only have a single group, as it would show an unnecessary tab. See Group Source Code Blocks for more information.

hidden

Hides the example. This is necessary for TypeScript counterparts of Java examples, which are only included to load component styles, as described in Client-Side Dependencies of Java Examples.

Snippets

Snippets — known as Tagged Regions in AsciiDoc — are segments of code examples. They’re displayed by default, instead of the entire source code of an included file.

The Expand code button in the example reveals all the lines of the source code. This can be useful for making it easier for readers to see the most relevant part.

For example:

Button button = new Button("Button");
Paragraph info = new Paragraph(infoText());
button.addClickListener(clickEvent -> {
    counter += 1;
    info.setText(infoText());
});
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/button/ButtonBasic.java[tags=snippet]
----

Use snippets by adding the tags attribute in the brackets following the path, indicating which named snippet to include (i.e., include::path/to/example[tags=snippet]).

Defining Snippets

Snippets are defined in the source code as comments: tag::snippet[] marks the start of the snippet; and end::snippet[] marks the end of the snippet.

The snippet name between the colons and square brackets can be any string. You don’t need to name it "snippet", but you need to use the string for both the starting and the ending comment.

Write the comment in the format defined for the example’s syntax highlighting. For TypeScript examples, this means that code tagged with [source,html] must use HTML comments within an HTML literal in the code. Here are examples for commonly used languages in the documentation:

public MyComponentExample() {
  // tag::snippet[]
  MyComponent myc = new MyComponent("My component");
  // end::snippet[]
  add(myc);
}

Excluding Source Code Lines

Part of the source code can be entirely omitted from the rendered code examples by appending a specific comment after the line (i.e., hidden-source-line). For example, here’s how you would hide an annotation in a Java example:

// The following line isn't shown
// if this file is added using the include directive
public MyComponentExample() {
  MyComponent myc = new MyComponent("My component");
  add(myc);
}
// The following line isn't shown
// if this file is added using the include directive
// tag::highlight[]
@SomeAnnotation // hidden-source-line
// end::highlight[]
public MyComponentExample() {
  MyComponent myc = new MyComponent("My component");
  add(myc);
}

This is useful for excluding code that’s only needed for a rendered UI example, but not relevant for normal use of a component.

This isn’t a standard AsciiDoc feature, but an extension that Design System Publisher provides.

Group Source Code Blocks

When you combine related code examples together, they can be grouped to create collections of related source files by including the group attribute in the include directive. Each group can include a description for its contents written before each source code block.

A common use case for the grouping is to show alternative approaches for achieving the same end result. For example, how to implement a certain user interface in either Java or TypeScript:

Here’s some text to describe the TypeScript example. You could mention things like the Person.ts file.

<vaadin-virtual-list
  .items="${this.people}"
  ${virtualListRenderer(this.personCardRenderer, [])}
></vaadin-virtual-list>
[.example]
--
Here's some text to describe the TypeScript example.
You could mention things like the `Person.ts` file.
[source,html]
----
include::{root}/frontend/demo/component/virtuallist/virtual-list-basic.ts[group=TypeScript,tags=snippet,indent=0]
----
[source,typescript]
----
include::{root}/frontend/generated/com/vaadin/demo/domain/Person.ts[group=TypeScript]
----
[source,typescript]
----
include::{root}/frontend/generated/com/vaadin/demo/domain/Address.ts[group=TypeScript]
----

Here's some text to describe the Java example.
You could mention things like the `setRenderer()` method.
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/virtuallist/VirtualListBasic.java[group=Java,tags=snippet,indent=0]
----
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/domain/Person.java[group=Java]
----
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/domain/Address.java[group=Java]
----
--

Rendered UI Examples

You can render a code example as an interactive UI example by including the render attribute in the include directive. Only Java and TypeScript examples can be rendered. The following is an example of a rendered Java example:

Open in a
new tab
package com.vaadin.demo.component.button;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.Route;

@Route("button-basic")
public class ButtonBasic extends Div {
    private int counter = 0;

    public ButtonBasic() {
        // tag::snippet[]
        Button button = new Button("Button");
        Paragraph info = new Paragraph(infoText());
        button.addClickListener(clickEvent -> {
            counter += 1;
            info.setText(infoText());
        });
        // end::snippet[]

        HorizontalLayout horizontalLayout = new HorizontalLayout(button, info);
        horizontalLayout.setAlignItems(FlexComponent.Alignment.BASELINE);
        add(horizontalLayout);
    }

    private String infoText() {
        return String.format("Clicked %d times", counter);
    }

}
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/button/ButtonBasic.java[render]
----
Client-Side Dependencies of Java Examples

Styles for components that are used in an example are only loaded for TypeScript examples. You therefore always need a TypeScript counterpart for any Java example. Otherwise, the components aren’t styled and the example appears broken. The TypeScript example doesn’t need to be functional, it only needs to import the needed components.

The TypeScript include shouldn’t have a group parameter, and it should have the hidden parameter instead of render.

For example, the AsciiDoc source for the previous example is actually the following:

[.example]
--
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/button/ButtonBasic.java[render]
----
[source,typescript]
----
include::{root}/frontend/demo/component/button/button-basic.ts[hidden]
----
--

Alternatively, you can use the DOCS_IMPORT_EXAMPLE_RESOURCES="true" configuration option to load all frontend resources up front. Then it’s sufficient to include the Java code example without an additional, or hidden TypeScript example. Add it as before the start-up or build command:

DOCS_IMPORT_EXAMPLE_RESOURCES="true" npm run dspublisher:start

Use code example groups to show both a TypeScript and Java implementation for the same UI. For example, here’s the same example as before, now with a corresponding TypeScript version included.

Open in a
new tab
package com.vaadin.demo.component.button;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.Route;

@Route("button-basic")
public class ButtonBasic extends Div {
    private int counter = 0;

    public ButtonBasic() {
        // tag::snippet[]
        Button button = new Button("Button");
        Paragraph info = new Paragraph(infoText());
        button.addClickListener(clickEvent -> {
            counter += 1;
            info.setText(infoText());
        });
        // end::snippet[]

        HorizontalLayout horizontalLayout = new HorizontalLayout(button, info);
        horizontalLayout.setAlignItems(FlexComponent.Alignment.BASELINE);
        add(horizontalLayout);
    }

    private String infoText() {
        return String.format("Clicked %d times", counter);
    }

}
[source,java]
----
include::{root}/src/main/java/com/vaadin/demo/component/button/ButtonBasic.java[render,group=Java]
----
[source,typescript]
----
include::{root}/frontend/demo/component/button/button-basic.ts[render,group=TypeScript]
----

Always Show Source Code

Use the show-code style to show the source code immediately, so that the user doesn’t need to click the Show code button to reveal it. You can apply the style on a standalone source code block, or an .example style open block.

Open in a
new tab
Button button = new Button("Button");
Paragraph info = new Paragraph(infoText());
button.addClickListener(clickEvent -> {
    counter += 1;
    info.setText(infoText());
});
// Source code block
// tag::highlight[]
[source,java,role="show-code"]
// end::highlight[]
----
...
----

// Example open block
[.example.show-code]
--
...
--

Always Hide Source Code

Use the render-only style to hide the source code. This doesn’t disable the example group tabs, which can still be used to choose which rendered example is shown.

Open in a
new tab
Button button = new Button("Button");
Paragraph info = new Paragraph(infoText());
button.addClickListener(clickEvent -> {
    counter += 1;
    info.setText(infoText());
});
// Source code block
// tag::highlight[]
[source,java,role="render-only"]
// end::highlight[]
----
...
----

// Example open block
[.example.render-only]
--
...
--

Writing Examples to be Rendered

Before you can add a rendered UI example on a page, you need to create the example itself.

Add dependencies for custom components to the pom.xml file the same way as a custom theme JAR. Components and themes can be included in the same dependency.

Java Examples

Place Java-based examples in sub-folders inside the src/main/java/ folder. Each example needs to be in its own file. To add examples for a new component, create a new folder inside the component folder with a Java file inside it. For example, see the MyComponentExample.java file:

my-docs
└── src/main
    └── java/com/vaadin
        └── demo
            └── component
                ├── accordion
                ⋮   ├── AccordionBasic.java
                ⋮   ├── AccordionDisabledPanels.java
                ⋮   ⋮
                ├── mycomponent
                ⋮   ├── MyComponentExample.java
                ⋮   ⋮
package com.vaadin.demo.component.mycomponent;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.router.Route;
import com.vaadin.demo.DemoExporter; // hidden-source-line
import com.example.MyComponent;

@Route("my-component-example") // (2)
public class MyComponentExample extends Div { // (1)

    public MyComponentExample() {
        MyComponent myc = new MyComponent("My component");
        add(myc); // (3)
    }

    // (4)
    public static class Exporter extends // hidden-source-line
        DemoExporter<MyComponentExample> {} // hidden-source-line
}
  1. A class that extends Div or some other container like VerticalLayout.

  2. with an optional @Route annotation with a route name that’s unique within the entire website.

  3. and a constructor that adds the desired UI as a child to the class.

  4. A static inner class that extends the DemoExporter class with a type parameter matching the example class.

Design System Publisher uses spring-boot-devtools to rebuild automatically the Java examples when you modify and save them — assuming you configured your editor or IDE to do so. Rebuilding the Java examples typically takes about thirty seconds, after which the page must be reloaded manually.

TypeScript Examples

Place TypeScript-based examples in sub-folders within the frontend/ folder. Each example needs to be in its own file.

my-docs
└── frontend
    └── demo
        └── component
            ├── accordion
            ⋮   ├── accordion-basic.ts
            ⋮   ├── accordion-disabled-panels.ts
            ⋮   ⋮
            ├── my-component
            ⋮   ├── my-component-example.ts
            ⋮   ⋮

To add examples for a new component, create a folder with a TypeScript file inside it. For example:

import '../../init'; // hidden-source-line
import { applyTheme } from 'generated/theme';
import { html, LitElement, customElement } from 'lit-element';

import '@my-org/my-component/my-component';

@customElement('my-component-example') // (1)
export class Example extends LitElement { // (2)
  protected createRenderRoot() { // (3)
    const root = super.createRenderRoot();
    applyTheme(root);
    return root;
  }

  render() { // (4)
    return html`
      <my-component>My component</my-component>
    `;
  }
}
  1. A @customElement annotation with a name that’s unique within the entire website.

  2. A class that extends LitElement.

  3. A createRenderRoot() method that calls the applyTheme(root) method. This applies your custom theme to the example.

  4. A render method that returns the HTML for the example.

TypeScript code examples don’t refresh automatically. The code example displayed below a rendered example isn’t refreshed when you edit the source AsciiDoc file in your text editor. To refresh the code example, the page’s text content needs to be re-saved for Design System Publisher to rebuild the page, and you need to reload the page.

6DF51E1C-15BB-4E15-A3C7-5C616B7BFC35