Skip to content

Modifying HTML attributes within a Nice Partial #13

@domchristie

Description

@domchristie

This might be more of a component architecture issue, but I thought I'd throw it out there.

Let's say we have the following Nice Partial for a card component:

<%# app/views/components/_card.html.erb %>
<% yield p = np %>
<%= content_tag :article, class: 'card rounded-lg' do %>
  <h2 class="font-lg font-bold"><%= p.yield :heading %></h2>
  <%= p.yield :blurb %>
<% end %>

… and we render some blog posts using that component:

<%# app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
  <%= render 'components/card' do |p| %>
    <% p.content_for :heading, post.title %>
    <% p.content_for :blurb, post.description %>
  <% end %>
<% end %>

Now, say we want to customize the card style these posts, by reducing the roundness of the corners and adding a card--post modifier. (We'll use both utility and BEM-style class names to demonstrate a couple of approaches.) Our desired output would be:

<article class="card card--post rounded-sm">
  <h2 class="font-lg font-bold">Hello, world!</h2>
  Lorem ipsum
</article>

This is quite tricky since, we need to:

  1. add card--post without clobbering the default class (card)
  2. replace rounded-lg with rounded-sm

To satisfy 1., a nice API might look as follows:

<%# app/views/components/_card.html.erb %>
<% yield p = np %>
<%= content_tag :article, class: ['card', 'rounded-lg', local_assigns[:class]] do %>
  <h2 class="font-lg font-bold"><%= p.yield :heading %></h2>
  <%= p.yield :blurb %>
<% end %>
<%# app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
  <%= render 'components/card', class: 'card--post' do |p| %>
    <% p.content_for :heading, post.title %>
    <% p.content_for :blurb, post.description %>
  <% end %>
<% end %>

But how to we override or remove the rounded-lg class? One approach might be to remove the rounded-lg class from the partial, but this would require an adding rounded- classes to every instance.

Or if we're using Tailwind JIT, we could utilise the !important modifier:

<%# app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
  <%= render 'components/card', class: 'card--post !rounded-sm' do |p| %>
    <% p.content_for :heading, post.title %>
    <% p.content_for :blurb, post.description %>
  <% end %>
<% end %>

… but I'm wondering if there's a possible built-in solution? For example, could we implement something which mimics the DOMTokenList API:

<%# app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
  <%= render 'components/card', class: 'card--post' do |p| %>
    <% p.class_list.replace('rounded-lg', 'rounded-sm') %>
    <% p.content_for :heading, post.title %>
    <% p.content_for :blurb, post.description %>
  <% end %>
<% end %>

I'm also wondering if this could somehow apply to options_for, e.g.:

<%# app/views/posts/index.html.erb %>
<% @posts.each do |post| %>
  <%= render 'components/card', class: 'card--post' do |p| %>
    <% p.class_list.replace('rounded-lg', 'rounded-sm') %>
    
    <% p.content_for :heading, post.title %>
    <% p.options_for(:heading)[:class].remove('font-bold') %>
    
    <% p.content_for :blurb, post.description %>
  <% end %>
<% end %>

(It's been a while since I looked at #9, so this might not be possible to get this working at all!)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions