Unpoly: Compiler Selector Patterns

Updated . Posted . Visible to the public.

Quick guide for frequently used compiler selector patterns of Unpoly.

1. BEM Component Pattern

When: Reusable UI components with multiple child elements

Examples: toggleable.js, collapsible.js, searchable_select.js

up.compiler('.toggleable', (toggleable) => {
  const checkbox = toggleable.querySelector('.toggleable--checkbox')
  const content = toggleable.querySelector('.toggleable--content')
  // ...
})
%td.toggleable.-inverted.-ignore-when-not-empty
  .toggleable--content
    = form.text_field :something
  = form.check_box :option, class: 'toggleable--checkbox'

Naming:

  • Container: .component-name
  • Children: .component-name--element
  • Modifiers: .-modifier

Pros: Clear ownership, reusable, no namespace conflicts, modular
Cons: Verbose HTML, requires BEM knowledge

Preferred for ViewComponent

For views components we mainly rely on this pattern.


2. Simple Class Pattern

When: One-off, view-specific behavior

Examples: project_role.js, create_invoice_from_suggestion.js

up.compiler('.project-role', (projectRole) => {
  const deleteButton = projectRole.querySelector('#delete')
  // ...
})
%tr.project_role
  = form.text_field :description
  %td= tag.a delete_icon, id: 'delete'

Pros: Simple, quick to implement, natural HTML
Cons: Namespace collisions, fragile, unclear ownership


3. Attribute Pattern

When: Form field enhancements, configuration-driven components, semantic for elements that do not rely on any classes for styling

Examples: date_picker.js, session_check.js, planner.js, tooltips.js

// Simple boolean flag
up.compiler('[date-picker]', (element) => {
  const picker = flatpickr(element, { /* config */ })
  return () => picker.destroy()
})

// With configuration data (via up-data)
up.compiler('[planner]', (container, { type, view, icons }) => {
  window.Planner = new window.planners[type](JSON.parse(view), icons)
})
// Simple attribute
= form.text_field :start_date, 'date-picker' => true

// With configuration (see unpoly-data-passing.md for details)
%table.calendar{ planner: true, 'up-data': { type: @angle, view: @data }.to_json }

Pros: Semantic separation, configurable, HTML5 standard
Cons: Limited to single element, attribute clutter, no nesting convention

See Unpoly: Passing Data to Unpoly Compilers for detailed guide on passing configuration data


4. Generic Element Pattern

When: Universal enhancements for all elements of a type

Examples: form.js, input_type_number.js

up.compiler('form', (form) => {
  // Runs on EVERY form
})

up.compiler('textarea', (textarea) => {
  // Runs on EVERY textarea
})

Pros: No markup changes, consistent UX
Cons: Performance overhead, potential side effects, hard to customize

Profile picture of Felix Eschey
Felix Eschey
Last edit
Felix Eschey
License
Source code in this card is licensed under the MIT License.
Posted by Felix Eschey to makandra dev (2025-10-17 09:59)