Problem
- Inconsistent block CSS class names.
- Needlessly verbose block CSS class names - repeating "block" as suffix (originating from plugin ID).
Goal
- Revise block CSS class names for D8.
Proposed solution
-
Remove the leading module name from all block plugin IDs.
→ Code and CSS classes should rely on $plugin->getProvider().
-
Remove the trailing "_block" suffix from all block plugin IDs.
→ Needless duplication, both in plugin IDs as well as generated CSS classes.
-
Make the primary block CSS class "block-$provider-$pluginid".
→ Clean class names that cover all use-cases.
Note: Double-check plugin IDs of derivative plugins (e.g., menu blocks). To my knowledge, they are compatible and would work like this:
Plugin ID: base_plugin_id:derivative_id → menu:main CSS class: block-$provider-$baseid:$derivativeid → block-menu-menu--main
-
Remove the "block-$module" CSS class.
→ Rare use-case. If necessary, this can be achieved with a CSS3
[class*='block-module-']
selector.
.
.
.
Problem/Motivation
The Block HTML ID naming convention has changed from block-$module-$delta
to block-$instance_machine_name
. Since the $instance_machine_name
naming convention differs from the $module-$delta
pattern, the CSS selectors in core CSS and JS files no longer correctly apply styles to blocks provided by core.
Please note that #1896098: Add a plugin class to the blocks to identify instances in css addresses the issue of plugin-based class names on blocks. This issue's discussion and patches should be limited to the ID only of the block's HTML and associated CSS and/or JS.
Proposed resolution
To be determined, but should focus on the ID and not the class name, due to work being done in #1896098: Add a plugin class to the blocks to identify instances in css.
Remaining tasks
- Determine
$plugin
based naming convention for HTML IDs on blocks - Update CSS and JS selectors in core CSS and JS files (unsure which specific files would be affected)
User interface changes
None that I am aware of.
API changes
With an updated $plugin
based naming convention for HTML IDs on blocks, CSS and JS selectors will necessarily be updated in core files. Any ID-based CSS or JS selectors targeting blocks in contrib theme or modules will need to be updated (by the contributor).
Original report by effulgentsia
In #1535868: Convert all blocks into plugins, block HTML IDs have changed from block-$module-$delta
to block-$instance_machine_name
. The instance's machine name is administrator settable when initially creating the block instance (choosing "Configure" from the "Block Library"). The Standard profile includes several predefined block instances via config files in core/profiles/standard/config/plugin.core.block.*
. For example, the User login block instantiated in the Bartik theme has a machine name of login
, forming a config filename of plugin.core.block.bartik.login.yml
.
The machine names of these Standard profile blocks differ from the $module-$delta
pattern that existed prior to the conversion to plugins. For example, the User login block's machine name is login
, not user-login
.
But in several core CSS and JS files, we still have CSS selectors for #block-user-login
and other #block-$module-$delta
IDs.
At a minimum, we need to fix the CSS selectors to match the machine names, but are the machine names what we want them to be, and are we ok with them being administrator configurable?
Comment | File | Size | Author |
---|---|---|---|
#37 | interdiff.txt | 2.25 KB | benjy |
#37 | update-html-ids-1880646-36.patch | 5.5 KB | benjy |
#31 | interdiff.txt | 1.47 KB | benjy |
#31 | update-html-ids-1880646-31.patch | 3.99 KB | benjy |
#27 | interdiff.txt | 1.33 KB | benjy |
Comments
Comment #1
EclipseGc CreditAttribution: EclipseGc commentedI think this is a situation where we need to probably add default classes to the blocks and transition the css from ids to classes.
Comment #2
sunComment #3
pcambraMaybe related: #1896098: Add a plugin class to the blocks to identify instances in css
Comment #4
salvisThe machine names are very volatile. When you add a block, you can specify it's title, and the machine name changes with the title.
Once the title is localized, the machine name will probably follow, and the css id is broken right from the start.
#1896098: Add a plugin class to the blocks to identify instances in css fixes this issue by adding the
block-$pluginid
class:<div id="block-$machinename" class="block block-$module block-$pluginid">
This lets us select the div with
div.$module.$pluginid
Comment #5
star-szrCapping the acronyms :)
Comment #6
dcam CreditAttribution: dcam commentedhttp://drupal.org/node/1427826 contains instructions for updating the issue summary with the summary template.
Comment #7
adamdicarlo CreditAttribution: adamdicarlo commentedActually, going by that div you show, the selector would be
.block-$module.block-$pluginid
.Comment #8
Fidelix CreditAttribution: Fidelix commentedI think something like this would be clean, but still reliable:
<div id="block-$machinename" class="block $module $pluginid">
Or maybe:
<div class="block $machinename $module $pluginid">
Is there a reason to repeat the "block" string four times for the same element?
Comment #9
nod_tagging for html5/mobile initiative in case they have something to make this go somewhere.
Comment #10
catchRe-titling.
Comment #11
drifter CreditAttribution: drifter commentedAgree with Fidelix, if we leave out the block- prefix we gain a lot in readability and verbosity. Any name collisions should be rare and can be dealt with in various ways.
Comment #12
adamdicarlo CreditAttribution: adamdicarlo commentedAs far as targeting the blocks with JavaScript, decoupling HTML ID and CSS classes from the JS behavior hooks seems to be an emerging best-practice.
CSS classes and HTML IDs should be used for styling, not for hooking in JavaScript functionality, right? I think we've all been stung by jQuery selectors that rely on HTML structure and classes, when the structure and/or classes are changed for visual reasons.
There are different ways to decouple. Attribute data-js or data-hook:
Feels a little hacky, given the long-winded selector.
Role.js (GitHub repo) extends jQuery/Zepto to add @selectors:
Thoughts?
Comment #13
tim.plunkettVery few of these blocks are addressed from JS. Most of this is styling.
Also #12 is a larger discussion than this issue.
Comment #14
nod_@12: #1090592: [meta] Use HTML5 data-drupal-* attributes instead of #ID selectors in Drupal.settings
Comment #15
benjy CreditAttribution: benjy commentedI think we should go ahead with below, as suggested by @Fidelix in #8.
<div id="block-$machinename" class="block $module $pluginid">
Comment #16
benjy CreditAttribution: benjy commentedInitial patch attached to match #15.
I've also attached another patch for testing which removes the if statement around the $block->id() call. Not sure why that's needed so let's see if the tests fail.
Comment #18
benjy CreditAttribution: benjy commentedOK so it seemed removing the if statement around the $block->id() call didn't break anything.
New patch is based from update-html-ids-1880646-16-test-remove-if-statement.patch and fixes the relevant tests.
Comment #19
mradcliffePatch makes expected changes and I don't see any syntax or standards issues.
Simplytest.me test:
- Added the Who's Online block to the Highlighted region.
- Added the Who's Online block to the Sidebar first region.
- Got valid HTML
+1 to RTBC
If a contrib module does something fancy like preview a block without an id, then I think that module could pass in a dummy id. I don't think the conditional is necessary anymore either.
Comment #20
benjy CreditAttribution: benjy commentedNew patch attached replaces ":" with -- for plugin_ids as suggested by tim.plunkett.
Comment #21
benjy CreditAttribution: benjy commentedUpdated tests.
Comment #23
benjy CreditAttribution: benjy commentedBack to NR.
Comment #24
tim.plunkettThe changes look good.
This used to be titled "HEAD currently contains CSS selector regressions", and I hope this satisfies that need.
Comment #25
Dries CreditAttribution: Dries commentedCalling
str_replace()
in preparation of callingdrupal_html_class()
looks like a hack. Shouldn't that string replace operation live insidedrupal_clean_css_identifier()
, which is called bydrupal_html_class()
?Comment #26
benjy CreditAttribution: benjy commentedI'm not against changing the logic of drupal_clean_css_identifier() to replace colons with a double hyphen.
That would however be an API change? I'll put a patch up later and see if it breaks anything.
Comment #27
benjy CreditAttribution: benjy commentedNew patch moves the str replace into drupal_clean_css_identifier(), let's see what the testbot says.
Comment #29
Jeff Burnz CreditAttribution: Jeff Burnz commentedShould we call
drupal_clean_css_identifier()
directly and use its second argumentdrupal_clean_css_identifier('this:string', array(':' => '--'));
Or add to the $filter array in
drupal_clean_css_identifier()
?Comment #30
benjy CreditAttribution: benjy commentedI think we should add ":" => "--" to the default filter array in drupal_clean_css_identifier().
Comment #31
benjy CreditAttribution: benjy commentedNew patch attached adding to the default filters.
Comment #33
sunThe proposed solution of:
will cause plenty of false-positives when using external JS libraries and possibly CSS libraries.
Both $module and $pluginid are using arbitrary names that may be unique within Drupal, but not universally unique in the frontend space.
Aside from that, adding a built-in ":" → "--" conversion sounds like a very good idea; potentially useful elsewhere.
Comment #34
Jeff Burnz CreditAttribution: Jeff Burnz commentedIs there an argument for keeping $module?
Other than perhaps block menus they look like an edge case, i.e. block-system, block-custom-block do not seem very useful.
Can it be argued that $module can be selected from the $plugin_id using attribute selectors, e.g.:
Are plugin_id's that reliable?
Could something as simple as that work, i.e.:
<div id="block-$block_id" class="block block-$plugin_id">
Comment #35
sunGreat idea, @Jeff Burnz!
I continue to forget that we're able to leverage modern selectors now, so #34 sounds like a very nice and good compromise.
I agree that the use-case for styling all blocks of foo.module is very limited and can definitely be achieved with partial-match attribute selectors.
In fact, this approach resolves yet another hidden issue that existed in the past: Even just the
$module
and$[plugin_]id
sometimes led to ambiguous name clashes, because$[plugin_]id
was not namespaced with$module
.The new $plugin_id are always prefixed with $module to my knowledge.
+1 :)
Comment #36
benjy CreditAttribution: benjy commentedOK I've removed the module name altogether from the block class name. The comment about menu blocks, I guess that's helped somewhat in that they get the extra class block-menu.
#34 suggested:
<div id="block-$block_id" class="block block-$plugin_id">
How about:
<div id="block-$block_id" class="block $plugin_id">
I'm not sure how the block prefix to plugin Id helps us. plugin_id's seem to have the word block in anyway if it was clarity we were after? eg
system-menu-block--tools
andsearch-form-block
.If we were adding the word block so we could use attribute selectors for module provide blocks without clashes eg to match block-search than i'd rather just keep the module name as it's own class.
Personally I think it's more readable to have
.block.system
over[class*="block-system"]
in your CSS.Comment #37
benjy CreditAttribution: benjy commentedPatch shows my first suggestion without the module name at all and without the block prefix.
On a side note, it's really annoying that you can't preview your comments anymore because you're editing the issue.
Comment #39
Jeff Burnz CreditAttribution: Jeff Burnz commentedI'm not totally sure how plugin_id works, if block is a required suffix etc, we can follow #1879496: Do we need to worry about plugin ID collisions?, I know I have prepended module name and appended block in a test module I wrote, unless this in enforced programatically the we can't really guarantee it will alway be there. Someone who knows more about this needs to comment.
Comment #40
tim.plunkettThat's a coincidence:
Comment #41
Jeff Burnz CreditAttribution: Jeff Burnz commentedThanks tim.plunkett, so yeah, not reliable enough to ensure uniqueness. Even if core sorts this inconsistency out contrib will not, that's a given.
Shifting focus to the id, and wondering if we might be able to remove it, found #1989568: Remove block config ID from being used as an HTML ID or template suggestion.
The only reason I can think of to keep the id is as a fragment identifier?
$variables['elements']['#block']->id()
could be a class? EclipseGc actually hints at this #1. I tend to think this fits our idea of BEM better, a unique class per block instance rather than an id. No need for id or chained selectors in CSS.The fragment identifier is kind of a big deal imo, I wonder if there are any contrib modules that currently rely on this?
Comment #42
Fidelix CreditAttribution: Fidelix commentedIf that's the case I think we should go with:
<div id="block-$block_id" class="block $plugin_id">
Comment #43
tim.plunkett"recent_comments"
That is not prefixed. There is no requirement that they be prefixed.
Comment #44
benjy CreditAttribution: benjy commentedYeah I agree with the fragment identifier that's a big one.
What about selecting the block in JavaScript. Id's are still quite a bit faster to select than classes?
Comment #45
Jeff Burnz CreditAttribution: Jeff Burnz commentedRecap:
We need a way to apply CSS:
Targeting blocks by module "sounds" like a good idea, but in practice it's rarely done. However, if we have the module in the plugin_id this is entirely doable using attribute selectors. If you really need module as a separate class then you can do it in your theme.
This issue could be highly dependant on #1879496: Do we need to worry about plugin ID collisions? , where we can clean things up by adding the module prefix to all plugin id's that don't have it, and remove -block suffix, hopefully. We will then we set a de facto standard for contrib and get nice clean, usable classes. I read into tim.plunketts posts above that there is no technical reason why this cannot happen.
Un-prefixed block classes are prone to "false positives" because contrib and the wider front end world is unknowable. For example users could install a module with a library and it could break their site/design. It's also way more readable in CSS when you see "block-search" as opposed to just "search" and it negates the chained selectors.
So, if #1879496 lands we can do this:
<div id="block-$block_id" class="block block-$plugin_id">
Example selectors:
@benjy afaict, not really much faster, but the fragment and being able to avoid chained selectors are pretty compelling reasons to keep the id I think.
Comment #46
Fidelix CreditAttribution: Fidelix commentedSorry but I don't agree that avoiding false positives is a good enough reason to apply the "block-" prefix to all blocks.
I believe these clashes would be quite rare and should be dealt with individually.
Comment #47
Jeff Burnz CreditAttribution: Jeff Burnz commented@Fidelix, I hear your passion, but may I ask what do we gain from not having prefixes? That's the bit I don't really understand in your argument.
Comment #48
Fidelix CreditAttribution: Fidelix commentedIMHO, we get a cleaner, easier to understand HTML.
Overall, it's one of many small steps for improving the default markup, but I believe it helps.
Comment #49
joelpittetFew things to consider: (some of which is addressed above by a few people)
The IDs can be useful in certain use-cases but in general don't help styles as most front-end devs are moving to class selectors for specificity and ability to override.
I strongly believe if needed it should be easier to define the ID, ala a field that sets the ID of that block instance, or some automatic way. But doing the automatic work for something that may not be needed and just adds bulk the markup may be counter productive. @benjy mentioned a possibility of "Off, Auto, Manual" and I'd suggest "Off" by default.
Auto-generated ones will need to care about duplicate blocks which adds a bit tricky in being consistent and semantic for theming and moving code between dev/stage/prod environments.
I'd like to recommend, turn them "Off" by default the HTML IDs. Allow people who need them to easily manually set in the UI, prepare/preprocess hooks, or in the template and optionally auto-generate as before.
This is part to keep features we have already for those who depend on them and part to set sane and clean defaults.
@EclipseGc and @Jeff Burnz does that fall in line what you mentioned in #1 and #41?
And to a lesser note, I'd prefer to avoid name conflicts especially on generated classes, so prefixing. It's a bit cleaner without prefixing I agree but without it there will need to be more nested selectors to avoid the conflicts which will be annoying to debug and mildly slower to parse by the browser.
Comment #50
Jeff Burnz CreditAttribution: Jeff Burnz commented#48, OK, so you're talking about HTML, I am more thinking about selectors and how easy they are to understand in the stylesheets.
For example when reviewing a co-workers stylesheet and I see
.recent-comments {...}
I don't know if it's selecting a block, field, view or some other entity type, i.e. it has no context.Remember, I'm saying we should get rid of the silly -block suffix that most core blocks have insisted on. I'll even write the freaking patch if I have to. I hate that thing!
Comment #51
Fidelix CreditAttribution: Fidelix commentedI understand you, Jeff, and I agree that it would be more difficult to understand context if the css was like
.recent-comments {...}
However, it is the job of your co-workers to apply context where it's needed:
.block.recent-comments {...}
and we should say that in the CSS coding best practices docs. IMHO there is nothing wrong with chaining selectors like that, and as you know it's a standard practice of most modern css frameworks and libraries.@joelpittet:
You meant chained selectors? That's what they're for, what you call "conflict" is actually sharing attributes and like I said that's a good thing; and every frontend developer should know how to take advantage of that.
It's not harder to debug in 2014, a time we have excellent CSS/JS inspectors/debuggers in every major browser.
About parse performance... the impact is literally negligible. Even on low-end devices.
Comment #52
Fidelix CreditAttribution: Fidelix commented@joelpittet
About turning on/off IDs...
I think having that in core would be very confusing.
But there's nothing preventing us from giving contrib the ability to preprocess that. This honestly belongs to a module like https://drupal.org/project/clean_markup
It's a great initiative and it needs help.
Comment #53
Jeff Burnz CreditAttribution: Jeff Burnz commentedAre we not duplicating discussion and code from: #1896098: Add a plugin class to the blocks to identify instances in css A lot seems to be covered there.
What is still relevant from the OP and is this still critical? Isn't this just about the ID and other issues in the OP?
Time to rewrite the issue summary and refocus?
Comment #54
joelpittet@Fidelix thanks, feel old, chaining never used to work well cross-browser so it's on my internal "ignore this feature till it gets sorted" list.
Comment #55
Amber Himes MatzComment #56
Amber Himes MatzUpdated the issue summary as suggested by Jeff Burnz in #53.
Comment #57
Amber Himes MatzRemoved Needs issue summary update tag as I just updated the issue summary.
Comment #58
catchPer #53 I'm marking duplicate of #1896098: Add a plugin class to the blocks to identify instances in css.
Comment #59
sunHm. While #1896098: Add a plugin class to the blocks to identify instances in css does indeed contain a patch, this issue holds a much more in-depth discussion...
Given the block plugin ID list in #40, I think we have these concrete tasks:
Remove the leading module name from all block plugin IDs.
→ Code and CSS classes should rely on $plugin->getProvider().
Remove the trailing "_block" suffix from all block plugin IDs.
→ Needless duplication, both in plugin IDs as well as generated CSS classes.
Make the primary block CSS class "block-$provider-$pluginid".
→ Clean class names that cover all use-cases.
Note: Double-check plugin IDs of derivative plugins (e.g., menu blocks). To my knowledge, they are compatible and would work like this:
Remove the "block-$module" CSS class.
→ Rare use-case. If necessary, this can be achieved with a CSS3
[class*='block-module-']
selector.If that's the final set of tasks (and if we agree, of course :)), then let's create issues for those :)
(3) would be the existing #1896098: Add a plugin class to the blocks to identify instances in css, of course.
Comment #60
Fidelix CreditAttribution: Fidelix commentedI still disagree with this:
I believe this "block-" prefix is unnecessary.
Otherwise, I'd say we have a clear path forward, thanks Daniel.
Comment #61
sunRe-opening and turning into a meta issue, since #59 keeps on getting referenced elsewhere (now in issue summary).
Comment #62
BerdirI disagree with #58. Unless we somehow change the plugin discovery to be namespaced by provider, we have to prefix them by module, or we risk clashes. Looking at the list in #40, I can only see one example that was not prefixed by module name and that was changed to a view in the meantime.
Removing _block suffix seems like a useful thing, but I'm not sure if that's worth an API/storage change at this point in time?
So, as it was asked a year ago, what is still really necessary and doable here?
Comment #63
catchI think this is the right priority/status at the moment, I also am not at all clear what needs doing, and this isn't critically breaking anything.
Comment #64
EclipseGc CreditAttribution: EclipseGc commentedSeems like, we should generate ids by the machine name (since that has to be unique) and classes by the plugin id (since that can be placed multiple times). What is this issue about again?
Eclipse
Comment #65
tim.plunkettComment #68
tim.plunkettNot actively part of the Blocks-Layouts work.
Comment #70
finalred CreditAttribution: finalred commented3years “block-” still exist...
Comment #71
cilefen CreditAttribution: cilefen commentedIt is fine to complain but better to update the issue summary, providing justification to reopen the issue. In #63 a committer questioned the issue an no one has updated it.
Comment #77
devkinetic CreditAttribution: devkinetic at CommonPlaces Interactive commentedThis is still a thing, and in the latest releases more things are breaking, specifically views utilizing IDs which is a lot of modules, especially when related to javascript enabled layouts.
Comment #82
smustgrave CreditAttribution: smustgrave at Mobomo commentedBeen 9 years since this moved to PNMI. Should it be reopened or closed?