Skip to main content
Vuetify0 v1.0 is here
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Bulma

Bulma tells you to bring your own JavaScript. This package is that JavaScript.

Edit this page
Report a Bug
Open issues
Copy Markdown

IntermediateAug 24, 2026

Bulma is a CSS framework — it ships the .modal, .dropdown and .navbar styles and stops there, leaving every open, close, dismiss and keyboard interaction to you. @paper/bulma fills that gap: each component renders the markup Bulma documents, against the bulma.css you already load, with Vuetify0 supplying behavior, focus management and accessibility.

Nothing about your stylesheet changes. There is no theme to adopt, no class prefix to learn, and no CSS in the package.

Note

Reference: Bulma documentation↗︎ — the class catalog this package implements.

<script setup lang="ts">
  import {
    BuBreadcrumb,
    BuBreadcrumbItem,
    BuCheckbox,
    BuControl,
    BuDropdown,
    BuDropdownMenu,
    BuDropdownTrigger,
    BuField,
    BuInput,
    BuLabel,
    BuMenu,
    BuMenuItem,
    BuMenuLabel,
    BuMenuLink,
    BuMenuList,
    BuModal,
    BuModalBody,
    BuModalCard,
    BuModalDelete,
    BuModalFoot,
    BuModalHead,
    BuModalTitle,
    BuNavbar,
    BuNavbarBrand,
    BuNavbarMenu,
    BuNotification,
    BuNotificationDelete,
    BuNumberField,
    BuNumberFieldDecrement,
    BuNumberFieldIncrement,
    BuNumberFieldInput,
    BuPagination,
    BuPaginationEllipsis,
    BuPaginationItem,
    BuPaginationList,
    BuPaginationNext,
    BuPaginationPrev,
    BuPanel,
    BuPanelBlock,
    BuPanelHeading,
    BuPanelIcon,
    BuSelect,
    BuTab,
    BuTabList,
    BuTabPanel,
    BuTabs,
  } from '@paper/bulma'

  import { shallowRef } from 'vue'

  const nav = shallowRef(false)
  const modal = shallowRef(false)
  const notice = shallowRef(true)
  const tab = shallowRef('repos')
  const page = shallowRef(1)
  const section = shallowRef('Dashboard')
  const repo = shallowRef('bulma')
  const name = shallowRef('paper-bulma')
  const visibility = shallowRef('public')
  const watchers = shallowRef<number | null>(3)
  const privateRepo = shallowRef(false)
</script>

<template>
  <BuNavbar v-model="nav">
    <BuNavbarBrand :burger="false">
      <a class="navbar-item" href="#">
        <strong>Paper / Bulma</strong>
      </a>
    </BuNavbarBrand>

    <BuNavbarMenu>
      <div class="navbar-start">
        <a class="navbar-item">Dashboard</a>
        <a class="navbar-item">Documentation</a>
      </div>

      <div class="navbar-end">
        <div class="navbar-item">
          <div class="buttons">
            <button class="button is-primary" type="button" @click="modal = true">
              <strong>New repository</strong>
            </button>
          </div>
        </div>
      </div>
    </BuNavbarMenu>
  </BuNavbar>

  <section class="section">
    <BuBreadcrumb>
      <BuBreadcrumbItem href="#">
        Paper
      </BuBreadcrumbItem>

      <BuBreadcrumbItem href="#">
        Bulma
      </BuBreadcrumbItem>

      <BuBreadcrumbItem current>
        Dashboard
      </BuBreadcrumbItem>
    </BuBreadcrumb>

    <div class="columns">
      <div class="column is-one-quarter">
        <BuMenu v-model="section">
          <BuMenuLabel>General</BuMenuLabel>

          <BuMenuList>
            <BuMenuItem>
              <BuMenuLink value="Dashboard">Dashboard</BuMenuLink>
            </BuMenuItem>

            <BuMenuItem>
              <BuMenuLink value="Customers">Customers</BuMenuLink>
            </BuMenuItem>
          </BuMenuList>

          <BuMenuLabel>Transactions</BuMenuLabel>

          <BuMenuList>
            <BuMenuItem>
              <BuMenuLink value="Payments">Payments</BuMenuLink>
            </BuMenuItem>

            <BuMenuItem>
              <BuMenuLink value="Transfers">Transfers</BuMenuLink>
            </BuMenuItem>

            <BuMenuItem>
              <BuMenuLink value="Balance">Balance</BuMenuLink>
            </BuMenuItem>
          </BuMenuList>
        </BuMenu>
      </div>

      <div class="column">
        <BuNotification v-model="notice" color="info">
          <BuNotificationDelete />
          Bulma ships the classes. Escape, focus return, and the burger toggle
          come from Vuetify0.
        </BuNotification>

        <BuTabs v-model="tab">
          <BuTabList>
            <BuTab value="repos">Repositories</BuTab>
            <BuTab value="settings">Settings</BuTab>
          </BuTabList>

          <BuTabPanel value="repos">
            <div class="is-flex is-justify-content-space-between is-align-items-center mb-3 mt-4">
              <p class="title is-5 mb-0">{{ section }}</p>

              <BuDropdown menu right>
                <BuDropdownTrigger v-slot="{ attrs }">
                  <button class="button is-small" type="button" v-bind="attrs">
                    <span>Actions</span>

                    <span class="icon is-small">
                      <i aria-hidden="true" class="fas fa-angle-down" />
                    </span>
                  </button>
                </BuDropdownTrigger>

                <BuDropdownMenu v-slot="{ close, item }">
                  <a class="dropdown-item" v-bind="item" @click="close(); modal = true">New repository</a>
                  <a class="dropdown-item" v-bind="item" @click="close">Clone</a>
                  <hr class="dropdown-divider">
                  <a class="dropdown-item" v-bind="item" @click="close">Settings</a>
                </BuDropdownMenu>
              </BuDropdown>
            </div>

            <BuPanel v-model="repo">
              <BuPanelHeading>Repositories</BuPanelHeading>

              <BuPanelBlock value="bulma">
                <BuPanelIcon icon="fas fa-book" />
                bulma
              </BuPanelBlock>

              <BuPanelBlock value="paper">
                <BuPanelIcon icon="fas fa-book" />
                paper
              </BuPanelBlock>

              <BuPanelBlock value="v0">
                <BuPanelIcon icon="fas fa-code-branch" />
                vuetify0
              </BuPanelBlock>
            </BuPanel>

            <BuPagination v-slot="{ items }" v-model="page" class="mt-4" :pages="4">
              <BuPaginationPrev />

              <BuPaginationNext />

              <BuPaginationList>
                <template v-for="(item, index) in items" :key="index">
                  <BuPaginationItem v-if="item.type === 'page'" :value="item.value" />

                  <BuPaginationEllipsis v-else />
                </template>
              </BuPaginationList>
            </BuPagination>
          </BuTabPanel>

          <BuTabPanel value="settings">
            <div class="mt-4">
              <BuField>
                <BuLabel>Repository name</BuLabel>

                <BuControl>
                  <BuInput v-model="name" />
                </BuControl>
              </BuField>

              <BuField>
                <BuLabel>Visibility</BuLabel>

                <BuControl>
                  <BuSelect v-model="visibility">
                    <option value="public">Public</option>
                    <option value="private">Private</option>
                    <option value="internal">Internal</option>
                  </BuSelect>
                </BuControl>
              </BuField>

              <BuField>
                <BuLabel>Watchers</BuLabel>

                <BuNumberField v-model="watchers" :max="99" :min="0">
                  <BuNumberFieldDecrement />
                  <BuNumberFieldInput expanded />
                  <BuNumberFieldIncrement />
                </BuNumberField>
              </BuField>

              <BuField>
                <BuControl>
                  <BuCheckbox v-model="privateRepo">Make this repository private</BuCheckbox>
                </BuControl>
              </BuField>
            </div>
          </BuTabPanel>
        </BuTabs>
      </div>
    </div>
  </section>

  <BuModal v-model="modal">
    <BuModalCard>
      <BuModalHead>
        <BuModalTitle>New repository</BuModalTitle>
        <BuModalDelete />
      </BuModalHead>

      <BuModalBody>
        <BuField>
          <BuLabel>Name</BuLabel>

          <BuControl>
            <BuInput v-model="name" placeholder="my-project" />
          </BuControl>
        </BuField>

        <BuField>
          <BuLabel>Visibility</BuLabel>

          <BuControl>
            <BuSelect v-model="visibility">
              <option value="public">Public</option>
              <option value="private">Private</option>
            </BuSelect>
          </BuControl>
        </BuField>
      </BuModalBody>

      <BuModalFoot>
        <div class="buttons">
          <button class="button is-success" type="button" @click="modal = false">
            Create
          </button>

          <button class="button" type="button" @click="modal = false">
            Cancel
          </button>
        </div>
      </BuModalFoot>
    </BuModalCard>
  </BuModal>
</template>

<style>
  /* The docs iframe is always below Bulma's 1024px navbar breakpoint, so the
     burger/drawer path would be the only one that ever runs. Force the desktop
     navbar so this reads as a dashboard, not a phone menu. */
  .navbar-menu,
  .navbar-start,
  .navbar-end {
    align-items: stretch;
    display: flex;
  }

  .navbar-menu {
    flex-grow: 1;
  }

  .navbar-start {
    margin-inline-end: auto;
  }

  .navbar-end {
    margin-inline-start: auto;
  }

  .navbar-item {
    align-items: center;
    display: flex;
  }
</style>

Installation

bash
pnpm add @paper/bulma bulma

Bulma itself is an optional peer dependency — load it from npm, from Sass, or from a CDN. The package never imports it.

main.ts
import { createApp } from 'vue'
import App from './App.vue'

import 'bulma/css/bulma.min.css'

createApp(App).mount('#app')

There is no plugin to install and no provider to mount. Import a component and use it:

vue
<script setup lang="ts">
  import { BuModal, BuModalClose, BuModalContent } from '@paper/bulma'

  import { shallowRef } from 'vue'

  const open = shallowRef(false)
</script>

<template>
  <button class="button is-primary" type="button" @click="open = true">
    Open modal
  </button>

  <BuModal v-model="open">
    <BuModalContent>
      <div class="box">Your content</div>
    </BuModalContent>

    <BuModalClose />
  </BuModal>
</template>

Every region Bulma documents is an express part component — BuModalContent, BuModalHead, BuDropdownMenu — never a named slot. You compose the markup you already know.

Note

Bulma 1.0+ only. The 0.9.x line predates Bulma’s CSS variables and is explicitly unsupported. Latest verified: 1.0.4.

What conformance means

Every component is diffed against the markup published on bulma.io↗︎. The fixtures live beside the source in packages/bulma/harness/fixtures/, captured verbatim from the documentation, and the conformance suite asserts that the rendered DOM matches — element for element, class for class.

Where the two differ, the difference is deliberate and declared:

DeviationWhy
No owned token namespace--bulma-* belongs to upstream; the package owns no prefix
Upstream state classes, unprefixedis-active and is-hoverable instead of data-attribute hooks — Bulma’s CSS selects on them
Native form controlsBulma styles native select, input[type=checkbox] and friends; a non-native control gets none of your CSS
Added ariaBuDropdownMenu emits role="menu" only when its items really are menu items[1]; BuNotificationDelete labels its delete button

Components

Grouped by Bulma’s own taxonomy. Each entry is the component you compose against; its regions ship as parts under it.


  1. Both additions fix axe failures in the verbatim documentation markup — the docs’ arbitrary-content dropdown promises role="menu" without menu items, and its delete button ships unlabeled. ↩︎

  2. Bulma documents no number input. BuNumberField composes one out of the form parts it does document — attached controls, buttons and an input — so its conformance fixture is self-authored rather than captured, and declares the upstream provenance of every class in it. ↩︎

Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/