# bootstrap_italia_flutter

> Bootstrap Italia, the design system of Italian public administration, as a Flutter package: 60 widgets, and a catalogue that runs in the page.

Case study by Paolo Gianfelici (Full-stack developer & UI/UX designer). https://paologianfelici.com/work/bootstrap-italia-flutter

- **Role:** Author and maintainer: the widgets, the tests, the catalogue app, the checks and the releases
- **Type:** Open-source Flutter package, MIT licence, on pub.dev
- **Period:** July 2026 – today (0.1.2, October 2026)
- **Platforms:** Android, iOS, web, macOS, Windows and Linux
- **Stack:** Flutter, Dart, Bootstrap Italia 2.18, Playwright, axe-core, GitHub Actions
- **pub.dev:** https://pub.dev/packages/bootstrap_italia_flutter
- **GitHub:** https://github.com/PaoloGi/bootstrap_italia_flutter

Bootstrap Italia is the design system that Italian public-sector websites are built on, and it exists as CSS and JavaScript only. bootstrap_italia_flutter brings it to Flutter: the same buttons, alerts, cards, forms, headers, megamenus and modals, for the apps that a public administration publishes next to its site.

I wrote it after bootstrap_italia_icons, the icon package I had already published, and put it on pub.dev in October 2026. It is deliberately unofficial: the name ends in _flutter, the README says it is not produced or endorsed by Developers Italia or AgID, and it does not claim accessibility conformance, which is a legal requirement for these services, until that has been tested with real assistive technology.

*bootstrap_italia_flutter is on pub.dev and GitHub. The recordings and the live example are its own catalogue app, built for the web: its texts are the package’s, in Italian, and nothing is sent anywhere.*

- 60: widgets exported: buttons, alerts, cards, forms, modals, headers, megamenus, carousels and more
- 1,055: automated tests, 369 of them contracts on what a screen reader is told
- 73/74: component states within 5% of the official kit’s own rendering
- 3: languages for the strings it speaks on your behalf: Italian, German and French

## A design system, read rather than eyeballed

### The kit’s look, from the kit’s CSS

Every colour, size, padding and border is read from the compiled CSS of Bootstrap Italia 2.18.0, not guessed from screenshots. The buttons, alerts and cards in the catalogue are the same widgets a consumer app gets, with the same tokens, and the fonts (Titillium Web, Lora, Roboto Mono) travel inside the package.

### Forms that work with a keyboard and a screen reader

Inputs, selects, checkboxes, radios, toggles and an autocomplete, each with its label, hint and error tied to the field. The autocomplete narrows its list as you type, highlights the match and announces how many results there are without moving the focus.

### Navigation and overlays that keep the focus

The megamenu opens a section into a panel of links and gives the focus back to the entry that opened it; the modal takes the focus, keeps it and closes on Esc. These are the parts that go wrong first when a design system is rebuilt for another toolkit.

## How it was built

### Accessibility checked in layers

Tests assert the roles and states in Flutter’s semantics tree; axe-core runs against this package and against the official React kit, so a problem that comes from upstream is never reported as mine; contrast and target sizes are computed from the tokens, because axe cannot judge contrast on a Flutter canvas; and on devices, the Android and iOS accessibility trees are read back, which is where the layers above stopped agreeing with the platform. None of the device checks run in CI, and the README says so.

### Honest about what it is and is not

There is a conformance document that states what has and has not been verified, a list of the components the kit has and the package does not (progress bars, steppers, timeline, pagination), and a closing section on each catalogue page for the parts of the documentation that mean nothing in Flutter, so a reader does not wonder whether they were missed.

### A promise that needed a test

The README says the package makes no third-party requests, which matters for GDPR in public administrations. A web soak test found that Flutter’s web renderer downloads Roboto from fonts.gstatic.com at startup whatever the app uses, unless the app declares a font family named exactly Roboto. The catalogue now bundles one, and the soak test fails if that request ever comes back.

## The catalogue, running in the page: The package’s own example app, live

A widget library is best judged by using it. The package ships a catalogue app that mirrors the official Bootstrap Italia documentation page by page, with the Dart that produced each example beside it; here it is, built for the web.

- **The published code, built for the web.** The repository at the 0.1.2 release, its example app built with flutter build web. The renderer is served from this site rather than from Google’s CDN, and the fonts are the package’s own.
- **Nothing from outside.** The recording runs with every outside request refused, and it found one: an invisible character in one catalogue text that no bundled font draws, which makes Flutter fetch a fallback font from Google. The copy shown here drops that character; the package is untouched.
- **Driven through the accessibility tree.** Flutter draws on a canvas, so there is nothing to click. A Playwright storyboard turns on the semantics tree, finds each widget by its accessible name, and touches it like a finger would; scrolling the sidebar to reach the lower entries is part of the script.
- **Two layouts.** At a desktop width the catalogue is a list and a page side by side; below the large breakpoint the list fills the screen and each page replaces it. The recordings show the first, the live example the second.

The texts are the package’s own, in Italian, following the official documentation. Bootstrap Italia and the Italian Republic’s visual identity belong to their owners; this package is a community port with no official status.

## Recordings

- **The components** (29 s): The catalogue, one page per component, laid out like the official documentation: a live example, then the code that makes it. https://paologianfelici.com/media/bootstrap-italia-flutter/clips/components.mp4?v=c6e4c3f5ae
- **Forms** (29 s): Inputs, a select and an autocomplete that suggests as you type: all keyboard-driven and announced as the kit asks. https://paologianfelici.com/media/bootstrap-italia-flutter/clips/forms.mp4?v=e11803652d
- **Navigation and overlays** (30 s): The megamenu, the modal and the notification: the parts that open over the page, with focus kept where a keyboard needs it. https://paologianfelici.com/media/bootstrap-italia-flutter/clips/navigation.mp4?v=8ce55ab16a
