Manual DOM class toggling versus reactive state binding for Bulma interactive components
0 reputation · 22 Nov 2021, 12:40 UTC
0 reputation · 22 Nov 2021, 12:40 UTC
Bulma provides interactive component structures—modals, dropdowns, tabs—entirely through CSS classes such as is-active. Because the framework ships without JavaScript, every state change must be reflected in the DOM by the consuming application.
A single-page application built with a reactive framework (React, Vue, Svelte) already owns the source of truth for component visibility. The developer must decide whether to let the framework drive Bulma classes directly through declarative bindings or to imperatively toggle classes via refs and effect hooks.
Determine which approach yields a maintainable synchronization boundary between the framework's state layer and Bulma's CSS-only expectations without introducing duplicate state or timing mismatches.
35775 reputation · 23 Nov 2021, 00:30 UTC
Prefer declarative class bindings driven by the framework's state. Bind is-active from a single boolean such as isOpen, and reserve imperative ref toggling for rare cases where code outside the framework owns the element. This keeps one source of truth and prevents Bulma's CSS from falling out of sync with the component lifecycle.
This assumes a React, Vue, or Svelte SPA using Bulma 0.9.x or 1.x, where Bulma supplies only CSS and the framework owns visibility. If a third-party Bulma JavaScript plugin also toggles is-active, the two writers will conflict regardless of the binding method.
Confirmed: Bulma ships no JavaScript, so modal, dropdown, and tab visibility depends entirely on classes such as is-active. Reactive frameworks reconcile the DOM from state, so declarative binding makes the DOM a pure reflection of that state.
Likely explanation: Imperative toggling through refs works for isolated clicks, but during rapid updates the framework's next render can overwrite manually set classes. During server-side rendering hydration, client-side imperative changes before hydration completes can cause class flicker or hydration mismatches.
Bulma's CSS does not manage focus, ARIA attributes, or keyboard behavior. After the framework applies is-active, run focus management in a lifecycle hook that executes after mount and after isOpen changes—for example, useEffect in React, onMounted in Vue, or onMount in Svelte. Ensure the modal or dropdown node exists in the DOM before attaching listeners or moving focus. Conditional rendering or a portal can guarantee DOM presence before the hook runs.
Yes. Refs bypass the framework's reconciliation cycle. If a rapid state update triggers a re-render while a ref-based handler is pending, the framework may restore the class derived from state and overwrite the manual toggle. With SSR, the server HTML reflects server state; imperative client toggles before hydration can produce a mismatch between the hydrated DOM and the client's first render, causing flicker or a hydration error. Declarative bindings eliminate this because the class is computed during every render pass.
Community practice wraps Bulma markup in a framework component that accepts isOpen or value and conditionally renders is-active. The wrapper centralizes ARIA attributes, focus management, and class logic so consumers treat it as a controlled component. Example pattern:
function BulmaModal({ isOpen, onClose, children }) {
return (
{children}
);
}
const [isOpen, setIsOpen] = useState(false).is-active declaratively from that state on every render.isOpen changes.If you are using server-side rendering with hydration, imperative toggling can cause hydration flicker before the client takes over. This does not change the core recommendation, but it raises the risk of manual DOM writes.
Use comments to ask for clarification. Post a solution as an answer.
5,890 reputation · 22 Nov 2021, 20:24 UTC
When a Bulma modal or dropdown relies on CSS transitions, conditionally unmounting the node on close removes it from the layout flow before the browser can animate the exit. Keeping the wrapper mounted and driving is-active from a single boolean preserves the transition lifecycle and gives focus-management hooks a stable element to query. This is distinct from the portal-or-conditional-render choice for initial mount: the node can be present in the server HTML but hidden, then activated on the client.
In React, that usually means rendering the modal shell unconditionally and letting isOpen control the class; in Vue or Svelte, the same pattern avoids v-if or {#if} teardown on every toggle. If you do unmount, expect the exit transition to be skipped unless you add a separate leave-animation layer. Verify your Bulma version first—v1 changed modal and dropdown markup relative to v0.x forks, so transition selectors may target different ancestors.
Practical check: toggle the boolean in dev tools and confirm the element remains in the DOM with the class flipping, rather than disappearing entirely. If the node vanishes before the transition ends, the CSS-only expectation is broken regardless of whether you use refs or declarative bindings.