<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://iamkulykov.com/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    
    <title>Serhii Kulykov</title>
    <link>https://iamkulykov.com/</link>
    <atom:link href="https://iamkulykov.com/feed.xml" rel="self" type="application/rss+xml" />
    <description>Serhii Kulykov writes about web components, accessibility and design systems.</description>
    <language>en</language>
    <item>
      <title>A11y Memoirs, episode 1. The Story of Vaadin Button</title>
      <link>https://iamkulykov.com/blog/a11y-memoirs-01-button/</link><description>&lt;p&gt;Recently, GitHub added highlighting for &lt;a href=&quot;https://github.blog/changelog/2026-10-01-accessibility-statements-highlighted-on-repository-overview/&quot;&gt;accessibility statements&lt;/a&gt; on the repository overview page. While adding &lt;a href=&quot;https://github.com/vaadin/web-components/blob/main/ACCESSIBILITY.md&quot;&gt;&lt;code&gt;ACCESSIBILITY.md&lt;/code&gt;&lt;/a&gt; for Vaadin components, I realized that there&#39;s so much more than just technical facts. I&#39;ve been working at Vaadin for many years, and accessibility support has been a recurring topic brought up by both my colleagues and our customers. And I do have a few stories to tell about it!&lt;/p&gt;
&lt;p&gt;Today, I&#39;m excited to share the first episode of my A11y Memoirs — a series of short blog posts covering challenges that we faced at Vaadin while developing a set of high-quality web components and making them accessible. For me, this journey started even before I joined the company, back when I was an external contributor.&lt;/p&gt;
&lt;p&gt;One of the first components I ever &lt;a href=&quot;https://github.com/vaadin/vaadin-button/pull/29&quot;&gt;contributed to&lt;/a&gt;, more than 9 years ago, was &lt;code&gt;&amp;lt;vaadin-button&amp;gt;&lt;/code&gt;. It was a completely different time, both for the emerging web components ecosystem with its early adopters and for the browser landscape in general. So I&#39;ll take a step back and start with some historical background.&lt;/p&gt;
&lt;h2&gt;Why not a plain old &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Initial versions of Vaadin Elements, like Combo Box, Date Picker and Upload, were inspired by Material Design and used the &lt;code&gt;&amp;lt;paper-button&amp;gt;&lt;/code&gt; Polymer element under the hood. This changed in 2017: while we were establishing a solid foundation for Vaadin Platform 10, our component set rapidly evolved to cover all basic needs.&lt;/p&gt;
&lt;p&gt;Back then, styling components was a challenge due to &lt;code&gt;::part()&lt;/code&gt; being just an early proposal and custom CSS properties not being able to cover all cases. Our team solved this by creating &lt;a href=&quot;https://github.com/vaadin/vaadin-themable-mixin&quot;&gt;ThemableMixin&lt;/a&gt; to inject CSS into components&#39; shadow roots. Our two original Vaadin themes, Lumo and Material, were built on top of it.&lt;/p&gt;
&lt;p&gt;Apart from styling consistency, there were other reasons for not using a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. Some of them were again related to browser limitations: Vaadin 10 supported Safari 9 and IE11, where CSS flexbox had a few known issues. In particular, &lt;a href=&quot;https://github.com/philipwalton/flexbugs#flexbug-9&quot;&gt;Flexbug #9&lt;/a&gt;: it wasn&#39;t possible to make native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; a flex container.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vaadin.com/docs/latest/components/button&quot;&gt;Vaadin Button&lt;/a&gt; supports &lt;code&gt;prefix&lt;/code&gt; and &lt;code&gt;suffix&lt;/code&gt; slots for placing icons and other supplementary content, as well as several shadow DOM parts, &lt;a href=&quot;https://cdn.vaadin.com/vaadin-web-components/25.3.0/elements/vaadin-button/#styling&quot;&gt;state attributes&lt;/a&gt; and &lt;a href=&quot;https://vaadin.com/docs/latest/components/button/styling&quot;&gt;variants&lt;/a&gt;. So the web component is here to stay, and for good reason. That said, Vaadin provides a Java API for the native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element, too.&lt;/p&gt;
&lt;h2&gt;Wrapping a button&lt;/h2&gt;
&lt;p&gt;The original version of &lt;code&gt;vaadin-button&lt;/code&gt; wrapped an internal clickable, absolutely positioned native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. This was a fairly common technique back in the day, when the &lt;a href=&quot;https://polymer-library.polymer-project.org/&quot;&gt;Polymer library&lt;/a&gt; was used to render templates in shadow DOM. There was also a shared &lt;a href=&quot;https://github.com/vaadin/vaadin-control-state-mixin&quot;&gt;ControlStateMixin&lt;/a&gt; used to handle focus, since &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/delegatesFocus&quot;&gt;&lt;code&gt;delegatesFocus&lt;/code&gt;&lt;/a&gt; and the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:focus-visible&quot;&gt;&lt;code&gt;:focus-visible&lt;/code&gt;&lt;/a&gt; pseudo-class were not yet implemented in browsers.&lt;/p&gt;
&lt;p&gt;By the way, whenever I see a new web component library or design system, one of the first things I check is how they implemented their button component. And yes, some libraries still do wrap a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; in different ways. It&#39;s not necessarily wrong now that shadow focus delegation is a thing. Years ago, it didn&#39;t work out for us.&lt;/p&gt;
&lt;p&gt;As part of significant accessibility improvements in Vaadin 22, we re-implemented the button component to use &lt;code&gt;role=&amp;quot;button&amp;quot;&lt;/code&gt; on the host. Later on, its logic was moved to &lt;code&gt;ButtonMixin&lt;/code&gt;, which is shared with a few other similar components. Our &lt;code&gt;vaadin-details&lt;/code&gt; uses it internally, too — and I&#39;ll cover it in a dedicated A11y Memoirs episode!&lt;/p&gt;
&lt;h2&gt;Tooltips and enabled state&lt;/h2&gt;
&lt;p&gt;If you inspect the DOM structure of &lt;code&gt;vaadin-button&lt;/code&gt; in DevTools (another thing I regularly do when exploring new component libraries), you&#39;ll notice that it has a &lt;code&gt;tooltip&lt;/code&gt; slot. This is a common API used in Vaadin for handling &lt;a href=&quot;https://vaadin.com/docs/latest/components/tooltip&quot;&gt;tooltips&lt;/a&gt; attached to different components, which is especially relevant for icon-only buttons.&lt;/p&gt;
&lt;p&gt;While the tooltip itself was added in Vaadin 23.3, it shared a limitation with the native &lt;code&gt;title&lt;/code&gt; attribute: it wasn&#39;t shown for &lt;a href=&quot;https://github.com/vaadin/flow-components/issues/4393&quot;&gt;disabled&lt;/a&gt; buttons. This was recognized as an accessibility problem and moved to a separate &lt;a href=&quot;https://github.com/vaadin/web-components/issues/4585&quot;&gt;enhancement ticket&lt;/a&gt;. Making disabled buttons accessible was a highly requested feature from our customers, and it was eventually implemented by my colleagues in the Design System team.&lt;/p&gt;
&lt;p&gt;In Vaadin 26, disabled buttons will be focusable and hoverable by default, which makes tooltips work and allows application authors to provide an explanation why a button is disabled. You can already enable this behavior in Vaadin 25 with the &lt;code&gt;window.Vaadin.featureFlags.accessibleDisabledButtons&lt;/code&gt; &lt;a href=&quot;https://vaadin.com/docs/latest/components/button#focus-hover&quot;&gt;feature flag&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Other caveats&lt;/h2&gt;
&lt;p&gt;There are a few more a11y memories to share about &lt;code&gt;vaadin-button&lt;/code&gt;. They illustrate the fact that proper accessibility isn&#39;t something you can just fix once: it has to be a continuous commitment from the team and requires attention to detail. This is what I appreciate the most about Vaadin&#39;s approach to UX and quality.&lt;/p&gt;
&lt;p&gt;The first one is about CSS. Vaadin components use a &lt;a href=&quot;https://vaadin.github.io/web-components/baseline.html&quot;&gt;baseline alignment&lt;/a&gt; approach that relies on a &lt;code&gt;::before&lt;/code&gt; pseudo-element with extra whitespace. This can cause unexpected announcements in some screen readers, such as NVDA. We fixed this by setting empty alternative text: &lt;code&gt;content: &#39;&#92;2003&#39; / &#39;&#39;&lt;/code&gt; — check out &lt;a href=&quot;https://www.sitelint.com/blog/hide-content-in-css-pseudo-elements-from-screen-readers&quot;&gt;Hiding content in CSS pseudo elements from screen readers&lt;/a&gt; for an explanation of this technique.&lt;/p&gt;
&lt;p&gt;Another issue causing incorrect announcements involved the internal elements wrapping the &lt;code&gt;prefix&lt;/code&gt; and &lt;code&gt;suffix&lt;/code&gt; slots that I mentioned earlier. To mitigate this, we decided to set &lt;code&gt;aria-hidden&lt;/code&gt; on &lt;a href=&quot;https://github.com/vaadin/web-components/pull/5049&quot;&gt;these elements&lt;/a&gt;. We expect application developers to make sure nothing essential is lost, or provide a meaningful &lt;a href=&quot;https://vaadin.com/docs/latest/components/button#aria-labels&quot;&gt;accessible label&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, an issue discovered and fixed quite recently involved the &lt;code&gt;.vaadin-button-container&lt;/code&gt; element, which again affected NVDA: in browse mode, key presses were not passed to the page. We &lt;a href=&quot;https://github.com/vaadin/web-components/pull/12525&quot;&gt;solved&lt;/a&gt; this in the latest releases of Vaadin 24 and 25 by setting &lt;code&gt;role=&amp;quot;presentation&amp;quot;&lt;/code&gt;. Lesson learned: nested DOM elements in button-like components should be avoided, or handled very carefully.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;That&#39;s it for the first episode of A11y Memoirs, which turned out to be a bit longer than expected. I&#39;d like to use this opportunity to share my passion for a11y and thank all my colleagues involved in the long process of designing, building and testing inclusive and accessible Vaadin components. Stay tuned for more!&lt;/p&gt;
</description><pubDate>Tue, 06 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Serhii Kulykov</dc:creator>
      <guid>https://iamkulykov.com/blog/a11y-memoirs-01-button/</guid>
    </item>
  </channel>
</rss>