Skip to content

Modifying HTML attributes within a Nice Partial #13

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!)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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