Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Proposal] Content utility for generating pseudo elements #1306

Closed
scottbedard opened this issue Jan 10, 2020 · 14 comments
Closed

[Feature Proposal] Content utility for generating pseudo elements #1306

scottbedard opened this issue Jan 10, 2020 · 14 comments

Comments

@scottbedard
Copy link
Contributor

The problem

With Tailwind, it feels ugly when I have to "leave Tailwind", and fall back to writing traditional css. One situation where I frequently find myself doing this adding empty content attributes to generate pseudo elements.

.selector:before {
  content: '';
  @apply block h-8 w-8 ...;
}

While not being the biggest deal, the mixed approaches feels off to me. I think it would be handy and more ergonomic if we could do something like this...

.selector:before {
  @apply block content-empty h-8 w-8 ...;
}

Pros

  • Lets us keep things concise and on one line
  • Avoid mixed styling (traditional css vs tailwind utilities)

Cons

  • One more thing to learn
  • Would be somewhat useless outside the context of pseudo elements

Again, not the most important utility, but I've found myself needing it on multiple projects, and think if would be handy to have out of the box.

@vricop
Copy link

vricop commented Jan 10, 2020

Using Tailwind class name conventions

What about this approach? It's what I'm working on for my scss utility library.

// style.css

/**
  * 1. Apply text content by retrieving the value from `data-content` HTML attribute. 
  *    If left empty fallback to an empty string.
  * 2. Property to apply to the generated content. `display`, `border`, etc.
  */
.before\:block::before {
  content: attr(data-content, ''); /* 1 */
  display: block; /* 2 */
}
<!-- index.html -->

<button class="before:block" data-content="👍">
  Like
</button>

@scottbedard
Copy link
Contributor Author

scottbedard commented Jan 10, 2020

Oh interesting, I always forget that the content attribute can be used with data attributes like that. I like this, but I wonder what a good name for the Tailwind utility would be then since "empty" is no longer accurate. Possibly just "content"?

<button data-content="🎉">
  Party!
</button>
button:before {
  @apply block content;
}

Update: It looks like the fallback content is not reliable yet, we might have to look into this a bit more to find a useful way for it to work with data attributes
https://developer.mozilla.org/en-US/docs/Web/CSS/attr#Browser_compatibility

Update 2: Actually since we'd just be defaulting to an empty string, this appears to work without relying on experimental properties.

content: attr(data-content);

https://jsfiddle.net/790yg4f8/

@vricop
Copy link

vricop commented Jan 10, 2020

It could be anything we want to: generated:display, content:display, content-before:display, content-after:display, etc. You name it. My favorite is before|false:display.

I forgot the second argument for fallback is no supported by any browser. I'm going to test it in codepen and I'll let you know what I came up with.

@vricop
Copy link

vricop commented Jan 10, 2020

Ok, found it

/**
  * 1. Fallback content
  * 2. Apply text content by retrieving the value
  *    from `data-content` HTML attribute.
  * 3. Property to apply to the generated content.
  *    `display`, `border`, etc.
  */
.before\:inline-block::before {
  content: ''; /* 1 */
  content: attr(data-content); /* 2 */
  display: inline-block; /* 3 */
}

If data-content is not set, fallback to the first content line.

<button class="before:inline-block" data-content="👍">
  Like
</button>

Update: attr(data-content) defaults to an empty string if the data-* attribute isn't found.
So we end up with:

.before\:inline-block::before {
  content: attr(data-content);
  display: inline-block;
}

Codepen demo

@vricop
Copy link

vricop commented Jan 10, 2020

Oh interesting, I always forget that the content attribute can be used with data attributes like that. I like this, but I wonder what a good name for the Tailwind utility would be then since "empty" is no longer accurate. Possibly just "content"?

<button data-content="🎉">
  Party!
</button>
button:before {
  @apply block content;
}

Update: It looks like the fallback content is not reliable yet, we might have to look into this a bit more to find a useful way for it to work with data attributes
https://developer.mozilla.org/en-US/docs/Web/CSS/attr#Browser_compatibility

Update 2: Actually since we'd just be defaulting to an empty string, this appears to work without relying on experimental properties.

content: attr(data-content);

https://jsfiddle.net/790yg4f8/

Yeap, you're right I've just found out about the fallback support. Your demo works fine, as does mine. Take your flavor 😁

@vricop
Copy link

vricop commented Jan 10, 2020

You are right, we don't need content: ''.

From the W3C spec

attr(X)
This function returns as a string the value of attribute X for the subject of the selector. The string is not parsed by the CSS processor. If the subject of the selector does not have an attribute X, an empty string is returned. The case-sensitivity of attribute names depends on the document language. )

@vricop
Copy link

vricop commented Jan 10, 2020

Instead of using @apply it could be implemented as a new variant. I don't know what @adamwathan thinks about this. It could be worth implementing. PurgeCSS can take care of the file size.

I'm always adding additional css because of the generated content tricks I like to do.

@scottbedard
Copy link
Contributor Author

Cool, I'm glad the default attr() behavior works to our advantage here! I wonder though, could this cause weird side effects for people? Is data-content something that people might already be using? It seems unlikely, but still might be something to consider...

@vricop
Copy link

vricop commented Jan 10, 2020

Cool, I'm glad the default attr() behavior works to our advantage here! I wonder though, could this cause weird side effects for people? Is data-content something that people might already be using? It seems unlikely, but still might be something to consider...

We could use data-please-dont-use-this-one or else! 😄

@scottbedard scottbedard changed the title Content utility for generating pseudo elements [Feature Proposal] Content utility for generating pseudo elements Jan 24, 2020
@scottbedard
Copy link
Contributor Author

scottbedard commented Jan 24, 2020

Maybe this could all just be part of the configuration, I can think of a couple custom content utilities I'd like to have...

content -> content: "";
content-data -> content: attr(data-content);
content-zwsp -> content: "\200B";
(^ for anyone curious, the last one is a zero width space, which is a handy way to prevent empty elements from collapsing)

Obviously we wouldn't need these all on by default, but I think it would be a helpful feature to have 🙂

@royvanv
Copy link

royvanv commented Feb 19, 2020

Cool, I'm glad the default attr() behavior works to our advantage here! I wonder though, could this cause weird side effects for people? Is data-content something that people might already be using? It seems unlikely, but still might be something to consider...

I think data-before or data-content-before are better candidates. I would be nice to add an option to prefix the data property (e.g. data-tw-before) or completely change it.

Also it should be possible to combine ::before and ::after elements:

.before\:inline-block::before {
  content: attr(data-before);
  display: inline-block;
}

.after\:inline-block::after {
  content: attr(data-after);
  display: inline-block;
}
<div class="before:inline-block after:inline-block" data-before="👍" data-after="👎">
  Can I get a like?   
</div>

@scottbedard Zero width spaces would be possible with &#8203; 😀

<div class="before:inline-block after:inline-block" data-before="&#8203;"></div>

@joeldbirch
Copy link

Again, not the most important utility, but I've found myself needing it on multiple projects, and think if would be handy to have out of the box.

I use pseudo-elements very often. After using Hucssley which makes styling pseudo-elements as easy as any other element, it's a pain to not have this in Tailwind projects.

(Full disclosure, I've contributed in a very minor way to Hucssley)

@scottbedard
Copy link
Contributor Author

I'm closing this issue, if anyone is looking for this functionality check out the following plugin
https://github.com/brandonpittman/tailwindcss-plugin-fancy/tree/master/packages/content

@CanRau
Copy link

CanRau commented Dec 21, 2021

For future reference, thanks to JIT we now have content-{value} utilities

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants