Thanks for your feedback. I think we should go settings.php then, because this way it is easy to create environment specific overrides there, e.g.
$settings['tailwind_jit_cache_lifetime'] = $_ENV['SOME_VARNISH_SETTING'];
I currently have some spare time and will try to implement this with a custom cache bin (which also has the side effect of getting a free override mechanism for the cache size)
Hello @garethhallnz and @stjuan627,
sorry for not giving feeback for so long. I've been testing your patches now, and I'm really impressed by the performance gain. I've been testing it on my own sites, and the speed boost was massive on sites with complex Tailwind files. I really like the work done here and I think many users will greatly benefit from this feature!
I'd like to reduce the interface a bit though. While the documentation is great, it might just be a bit to much for the average site admin, and the options seem to be a bit of academic nature, and not that essential in real operations.
Is there really any need to let the user pick different hashing algorithms? Even for ancient MD5, where collisions are easy to craft, they rarely happen naturally in real world. Since this is not a security related hashing, I can't see any situation where I'd advice a site admin not pick the fastest option.
And do we really need an additional cache lifetime setting here? Due Tailwind's atomic single responsibility classes, it is extremely unlikely that a cache hit will ever be outdated. Even if that edge case ever happens, a simple cache clear will solve the problem.
I don't think that the extra UI complexity of choosing hash algorithms and cache lifetimes will lead to better performance or less
problems in the wild. Therefore I think a simple on/off switch is enough.
Do you see any problems with removing hash or lifetime options?
Tailwind 4 now has a stable release, and it is "CSS first": All the configuration is now done in CSS files, and you no longer can use a tailwind.config.js without a CSS file. The way already recommended by this module for Tailwind v3 is now mandatory for Tailwind v4, if you still want to use a config file.
Therefore closing this issue, the currently recommended way is also the method recommended/mandatory method by TailwindCSS for the future.
Third party settings are not supported for simple configuration like theme settings (only for full config entities). The way Gin and Olivero currently do it for the shortcut module is a hack to get config schema validation. Contrib modules can not meaningfully use the hack, therefore closing this issue.
Maybe converting settings to full config entites (like `tailwind_jit.THEMENAME.yml`) is the correct way in the long run, so we can get config schema validation.
In response to #3 and #76:
The options of an URL object are documented as optional, which means NULL is a valid value.
So it does not really matter where the underlying problem is coming from. The problem is that the code blindly assumes that $options is a mandatory array, which does not comply with the spec. So any code in core that assumes that options is array, should be rewritten to be null-safe.
I got into this ticket due LinkGenerator, which has exactly the same incorrect not-null assumptions (line 93 and line 154)
So IMHO the underlying problem is not that somwhere the options aren't set, the underlying problem is that this code is not following the specification. I've added this to the issue summary.
This is fixed now in german translation, but most likely only because the translated texts now exist in german. The error should still exist in languages where those texts are not yet translated.
Hello maintainers,
thank you for this wonderful module. Rector patch looks super simple, and we have positive user feedback too. Would it be possible to get a new stable release with D11 compatibility?
TYVM
Yes, theme config is a bit of an edge case, but doesn't change the fact that third party settings never were supported, it just wasn't validated strictly enough.
This is a bit confusing statement, given we have prior art for this in multiple core themes, even the newer one's like Olivero and Gin (OK, Gin is not core, but at least part of CMS). But if this is the future direction, shouldn't we mark the corresponding code in shortcut and the themes as deprecated? This is what got me into this in the first place.
Use case for 3rd pary simple config is per-theme config settings. E.g. Tailwind JIT → module. Theme config is simple config.
2nd reason is it was quick and easy. The amount of boilerplate code required foryour own form is massive, compared to hooks if your module has just a very minimalistic one field config. It is often also is much better UX, if you add your setting to a form your users already know and thematically fits, instead of one more page in the config area.
A bit more digging:
Core's system.schema.yml
does not include a third_party_settings key in the system.site section.
ConfigFormBase::validateForm() calls $typed_config->validate(), which prevents the form from submitting.
Hacking a blanket third_party_settings: { }
key into core's schema file would at least allow a form submitting the values, but it still would not use my module's schema types or constraints.
Copying my entire schema into the core file would work.
For reference, my module's schema file:
system.site:
third_party_settings:
seasonal_paragraphs:
type: mapping
label: 'Summer season'
mapping:
from:
type: string
label: 'Summer season start'
constraints:
Regex:
pattern: '/^\d{4}-\d{2}-\d{2}$/'
message: "%value must be a date in YYYY-MM-DD format."
NotNull: []
to:
type: string
label: 'Summer season end'
constraints:
Regex:
pattern: '/^\d{4}-\d{2}-\d{2}$/'
message: "%value must be a date in YYYY-MM-DD format."
NotNull: []
My current workaround is creating a dummy form and disabling all form validation (override validateForm and don't call parent::validateForm()
). I can even use '#config_target' => 'system.site:third_party_settings.seasonal_paragraphs.to'
for form building... as long as I don't validate it...
Have not noticed any negative side effects. Cookies are of course still blocked until accepted.
From a user journey perspective I also like this behavior more than the instant banner: don't instantly nag the user, ask for permission after the user really interacts with the site.
There might be an odd edge case though: On a very small site without basically any content, paired with a very large screen, there might be no scroll bar, so the scroll event can not even be triggered and the banner will never be shown. But IMHO this is not a real world problem, improving the LCP for all users simply outweights this edge case. (And if necessary it can be easily fixed inside the admin UI.)
I've added a section about sticky buttons in the docs:
Document sticky action button behavior, see https://www.drupal.org/project/gin/issues/3488023 📌 Document clearly how contrib modules and/or site builders determine which content form buttons are primary Active and https://git.drupalcode.org/project/gin/-/blob/4.0.x/includes/form.theme?...
For Drupal libraries async and defer are almost meaningless, because - unless you are setting header: true
- Drupal libraries are at the end of the page anyways. Async can be even dangerous, because it would crash with Drupal's built in weighting / ordering of dependencies. Especially for Cookies, which is designed to be a dependency of other modules, I'd explicitly advise against async: This could break Drupal JS behaviors, and devs could no longer relyably use the well-known once('my_behavior', '.my_selector').doStuff()
pattern.
Generally I think this is topic a misunderstanding of (or maybe a general flaw in) Lighthouse. If you enable a Cookie Banner, then by design AND intention you'll have a late large contentful paint. If a site owner doesn't want the "paywall" style of the Cookie Banner, the admin has the option to show the banner after page scroll. This works well in 1.x on all our sites for LCP. In 1.x our problem was mostly FID (which was now replaced by INP, and where I have high hopes thanks to the Svelte rewrite. Really excited to try v2.x, thanks for all your work!).
Other similar tools simply use a small floating icon, which avoids the Lighthouse message by basically removing the largest
from the contentful paint
problem. (I personally find those even more annoying and distracting, especially on small screens. A banner at least is gone after declining.)
Maybe changing the default `lib_scroll_limit` config setting to 1 pixel would be a sensible default for most users?
hudri → created an issue. See original summary → .
This is not documented because it is not possible. If you want to add this feature to Drupal core, please help fixing issues 2142515 →
Hi ibraheem,
I've added some comments in your commit in the issue fork, please see
https://git.drupalcode.org/issue/tailwind_jit-3450150/-/commit/ec8ef5363...
There is no documentation because this simply does not exist. The module is just a "cookie banner", it only does the technical handling of injecting after consent. It does not document or store which users have given consent or when they have given consent.
AFAIK this is also not a requirement of the GDPR. This is something you might want to have to protect yourself in case of GDPR lawsuits, but AFAIK formally not a legal requirement of the GDPR (I'm no lawyer tough, so no guarantees here!)
And IMHO this module should not even do this, because then the module itself stores personalized data again, which opens a whole new can of worms. If you need this level of safety, a commercial CMP might be better suited for your case.
Fixed in version 1.1.2
Hi ibraheem,
thanks for your contribution. An UI input field for the tailwind.config.js would be nice and should work for all users. Just a few smaller things though:
- We would need to separate config files, one for HTML requests and one for Ajax requests. So we can disable Tailwind's preflight for Ajax requests to reduce the weight of the injected CSS.
- There is a typo in
$form['tailwind_jit']['additinal_config']
. Since we would need two inputs anyway, I would suggest somehint like$form['tailwind_jit']['ajax_config_file']
and$form['tailwind_jit']['html_config_file']
instead. - Could you please provide a MR instead on a patch file?
Thanks a lot
This is a known issue, and it is not possible to fix this in a meaningful way. Basically the architecture and philosophy of Tailwind and BigPipe are almost exact opposites:
- Tailwind JIT tries to generate a purged, minimized inline CSS on the server as early as possible for a perfectly optimized CSS with an ideal critical CSS rendering path.
- BigPipe renders blocks very late at page end with chunked streaming and injects them with client side JavaScript for a better perceived loading performance
I recommend to disable BigPipe completely. In my experience, the real word performance gain of BigPipe is neglectable. If you have blocks that are very slow, you could load them with "real" async Ajax requests, which a) work with Tailwind JIT and b) actually do deliver a real world performance gain.
Add warning / recommendation not to use services.yml in v10.3
@cyrusliew
Could you please test if the MR / patch fixes your problems?
https://git.drupalcode.org/project/tailwind_jit/-/merge_requests/8.patch
hudri → made their first commit to this issue’s fork.
Thanks for your report, I'll take a look at it.
On a side note, by architecture using BipPipe with Tailwind JIT has some limitations:
If you've got a page like
<div class="outer_container">
<span class="bigpiple_placeholder"> <!-- block will be injected here --> </span>
</div>
and CSS with child styles like
.outer_container .inner_style_from_a_bigpipe_replaced_block { ... }
then Tailwind JIT can not detect this inner style style due the structure of the source code. (Nevertheless the blocks need to be replaced properly, I'll check this.)
MR always removes the image loading setting from views plugin, which makes no sense in the context of an JSON or XML export
Fixed in v1.1.1
hudri → created an issue. See original summary → .
This bug is a bit annoying.
Contrib (e.g. Gin) is now copying this bug into their own codebase, just to make Config inspector shut up. And this again breaks theme 3rd party settings for everybody else :(
Olivero (and Gin) are incorrectly overwriting theme third party settings:
https://www.drupal.org/project/drupal/issues/3402885 🐛 [PP-1] olivero.settings config schema is wrong Postponed
"foo" is a path-relative link. Like domain.com/sub1/sub2/sub3 => domain.com/sub1/sub2/foo
"/foo" is a domain-relative link. Like domain.com/sub1/sub2/sub3 => domain.com/foo
Different things, both valid and both have their use cases.
In response to #156, nr. 1-3, please do NOT make any assumptions on link.
"random string" is a valid link. It will be url-encoded to "random%20string" though, but it is a perfectly valid relative url path. This is clearly a user mistake, not a bug. In our projects, we use and need all flavors of relative, absolute and schema links:
current-path-relative-links
/domain-relative-links
https://full-uri.com/link
whatsapp://send?phone=1234
mailto:john@doe.com
tel:01234567890
/taxonomy/[current_node_token:field_foo:entity:id]
Do we need the hard Drush dependency at all in the composer.json file? It is an optional CLI command, not a functional requirement. The module does it's job great via CLI and UI.
I'm proposing to completely remove the drush dependency from composer.json.
All patches and MR here are broken in 10.3.0 and don't work with jQuery UI modals.
#26 seems not relevant to this issue due I-Frame. The issue here is about CKeditor ballons in Ajax modals inside a simple < div >.
#27 does not work, the custom property --ck-z-modal
is no longer used in v10.3.0, nor does .ck-body-wrapper
have a CSS stacking context due missing position: relative;
or similar.
Also the jQuery UI modal receives an inline postion style top: ......px
. So even if we set position and z-index, the balloon is still incorrectly positioned from top.
Just for reference, this is how it looks on a non-sticky-table:
This example is from a multi-cardinality paragraph field. Both toggles (weight and columns) are rendered in source code, but only the weight toggle is used/visible (the column toggle is hidden, there is no responsive setting for this table)
Yes, I can still see the same issue on my test instance with 4.0.x, commit b95e4e3
Disabling the functionality is not a solution. This just hides the visual glitch, but also breaks the functionality of the responsive column toggle.
Tests from Drupal commerce users would be helpful, this would cross-check compatiblity with Inline Entity Form module.
I think this is the same as #3455080 🐛 Save button missing in modal for editing media using Media Library Edit module Fixed
I already have created a MR there, spare some time to test it?
My merge requests assumes that Gin content forms should never be used for Ajax requests, because forms in Ajax requests don't render the page template or the local actions block. This fixes it for the media library edit button, and theoretically this should also prevent similar problems with modules like Inline Entity Forms.
My assumption is a fairly broad approch, so feedback is welcome.
hudri → made their first commit to this issue’s fork.
Here is a patch file for RC11, suitable for composer patches:
drupal.org/files/issues/2024-06-19/gin_3270626_cherrypick_for_rc11.patch →
Just a cherry-pick of commit ee8cb77e refitted for RC11.
(Please do not use this patch file for issue development, use merge requests to resolve the issue)
This is by design. Tailwind JIT is operating on theme level, so it always should load last, after any CSS from parent themes or modules.
To solve your problem, use the @layer base { ... }
directive from Tailwind, which orders your CSS styles.
Or, if you don't want Tailwind's base styles at all, better do not include it in the Tailwind CSS input file:
@tailwind base; // <<< remove this line
@tailwind components;
@tailwind utilities;
Given the popularity of TailwindCSS and other utility class approaches, I think prefix and suffix are not sufficent. Prefix and suffix will cover many use cases, but taking the Tailwind syntax as an example, I would propose using a full regex to cover the following common use-cases:
Arbitrary values in Tailwind:
[100px,200px,300px] ==> min-h-[100px] or min-h-[200px] min-h-[300px]
Max or range breakpoints in Tailwind:
[sm,md,lg] ==> max-sm:hidden or max-md:hidden or max-lg:hidden
I've done something similar in my own custom style option plugin. Would love to see something similar in the module itself:
/**
* Defines the HTML attribute style option plugin.
*
* @code
* my_example_style_option:
* plugin: html_attribute
* # Preprocess the value with PHP's preg_place before rendering. E.g. massage an number input field
* # to a Tailwind CSS class. In the following example an input "1700" will be rendered as "max-w-[1700px]".
* value_preg_replace:
* pattern: '/^(\d+)$/'
* replacement: 'max-w-[${0}px]'
* @endcode
*
* @StyleOption(
* id = "html_attribute",
* label = @Translation("Html attribute")
* )
*/
class HtmlAttribute extends StyleOptionPluginBase {
/* ... */
public function build(array $build, string $region_id = NULL) {
/* ... */
if ($preg_replace = $this->getConfiguration('value_preg_replace')) {
$value = preg_replace($preg_replace['pattern'], $preg_replace['replacement'], $value, $preg_replace['limit'] ?? -1);
}
if (empty($value)) {
$value = 'INVALID_PREG_REPLACE_SUBJECT';
}
/* ... */
}
}
Full code here:
https://bitbucket.org/webtourismus/designsystem/src/master/src/Plugin/St...
I've been thinking about this, and I'm currently not planning to merge this.
First I really want to avoid any opinions on the file / directory structure in the module. Some themes might use a ./css subfolder, some themes might even reference and external designsystem completeley outside the theme path.
The 2nd thing is, the upcoming TailwindCSS v4 is planning a CSS-first configuration, which uses the CSS file also for configuration, instead of the tailwind.config.js file. So we might end up with a knob in the UI that is going to be deprecated (or at least not the recommended config method) in a few month.
Thanks for your contribution, I hope the module is still useful for your project.
hudri → changed the visibility of the branch 3445965-disable-message-fields to hidden.
hudri → changed the visibility of the branch 11.x to hidden.
I don't know if this bug can be reliably reproduced and test-cased because of
In code of this service is called readdir() function and the entries are returned in the order in which they are stored by the filesystem (according php documentation).
and
* Because order of templates (related theme hooks) depends on filesystem, it may vary on every environment.
Basically PHP's readdir()
has a built-in random behavior.
Given the unintrusive nature of this patch, can we add this without tests?
Ah, thanks for your feedback, I think I got.
But I'm still unsure if we can add this in the current form. If we merge this patch, we would loose the ability to use the @config
directive inside a CSS input file. The hardcoded --config=theme_directory/tailwind.config.js
from command line option would always overrule a @config "custom_tailwind.config.js"
directive inside a CSS file.
I don't think HTMX would help to solve any of this module's issues. For me, the main issues with CookiesJSR are (in order of significance)
-) The CC-ND licence of CookiesJSR. The VueJS variant also is CC-ND licenced. AFAIK I'm not even allowed to ship a patched version of CookiesJSR to my clients. This is a huge problem. So far I could work around patching CookiesJSR's source with "creative" CSS and JS, but for me any code using the CC-ND licence is a problem that needs to be replaced.
-) The jQuery dependency. In our newer v10 projects, Cookies is the only module left still using jQuery. This is a huge footprint just for cookie management. Rewriting the interactions between Drupal/the module and the client component to Vanilla JS would be a much larger performance boost for us, than switchting from React to Vue or whatever JS framework of the day.
Imagine a big gap here :-)
-) The external app / the external build step. To me CookiesJSR is something similar to a carousel JS lib like SplideJS: It is a quite isolated component with a lot of state and logic that needs to run on the client, and not on the server. So I think one externally build JS file is an acceptable tradeoff here.
E.g. I also had to patch SplideJS for my specific needs, but the build step is outsourced into a separate project, and multiple Drupal projects simply pull my patched SplideJS Git tag in composer.json. No NodeJS / extra built step required inside the Drupal project, the Drupal projects are still built- and deployable on any cheap vanilla PHP host. Could imagine doing the same with CookiesJSR.
HTMX primarly helps with client-server-interaction. Cookies just needs an initial initialization/config from the server, and then fully runs inside the client. IMHO this is a good architecture for this use-case, I don't want a server roundtrip after activating a service (group), it should run in the client. So I believe, even when core would switch to HTML, it would not change that much at all for this module. (Actually it should not change anything at all for this module, because we should use only Vanilla JS for interactions Drupal <==> client JS component).
Could you please explain why you need this? There already is the option to specify a tailwind config file by using Tailwind's @config
directive, you just need a single one-liner on top of your Tailwind file, e.g.
~/web/themes/custom/my_frontend_theme/my_tailwind_file.css
@config "tailwind.config.js"; /* <== add this on top of your file */
@tailwind base;
@tailwind components;
@tailwind utilities;
.my_custom_styles {
/* ... */
}
The advantage of this method is that it is not hardcoded, e.g. people using an external style library could pull in stuff from other folders.
Postponed due core's problems with 3rd party settings in themes.
I think I've got a related problem: My module has theme-specific settings. Previously I just my modules config settings into the root of theme.settings.yml. Inspired by the shortcut module, I now wanted to clean this up and move it into third party settings. Basically I wanted to update my config from
third_party_settings:
shortcut:
module_link: true
tailwind_jit:
compile_html_requests: true
html_input_file: 'foo.css'
compile_ajax_requests: true
ajax_input_file: 'bar.css'
to a proper schema
theme_settings.third_party.tailwind_jit:
type: mapping
label: 'Tailwind JIT settings'
mapping:
compile_html_requests:
type: boolean
label: 'Compile HTML requests'
html_input_file:
type: path
label: 'Uncompiled CSS input file for HTML requests'
compile_ajax_requests:
type: boolean
label: 'Compile Ajax requests'
ajax_input_file:
type: path
label: 'Uncompiled CSS input file for Ajax requests'
and proper third party settings
third_party_settings:
shortcut:
module_link: true
tailwind_jit:
compile_html_requests: true
html_input_file: 'foo.css'
compile_ajax_requests: true
ajax_input_file: 'bar.css'
The form hook is something like
function tailwind_jit_form_system_theme_settings_alter(&$form, FormStateInterface $form_state, $form_id = NULL) {
$form['third_party_settings']['tailwind_jit'] = [
'#type' => 'details',
'#open' => TRUE,
'#title' => t('Tailwind CSS Just-in-time compilation'),
'#tree' => TRUE,
'#parents' => ['third_party_settings', 'tailwind_jit'],
];
$form['third_party_settings']['tailwind_jit']['compile_html_requests'] = [
'#type' => 'checkbox',
'#title' => t('Compile HTML requests'),
'#default_value' => theme_get_setting('third_party_settings.tailwind_jit.compile_html_requests', $themeToBeConfigured),
];
/* ...more form elements here... */
}
This does not work correctly, there are two problems:
1) The schema is not used/validated, the boolean value is still stored as (int) 0 / 1 and not as (bool) true / false
2) When submitting the form, it saves all my values, but wipes the existing config from shortcut module.
Reading @berdir's comment, is shortcut a bad example? Should I avoid third party settings for themes at all?
This issue is closed in favor
#3447020
📌
Convert settings to third party settings
Active
. The module's settings are already living in code in YML in my_theme.settings.yml
. The linked issue describes the current schema, and the upcoming change.
hudri → created an issue.
I've been testing a bit, the changes from #1038316 📌 Allow for deletion of a single value of a multiple value field Fixed broke a lot of things in Paragraphs. I'm currently getting best results with patch from #2, in multi-lingual and nested scenarios. The patch from #5 has issues with nested paragraphs, in my case the < table >'s expanded horizontally far beyond the container < div >.
This also is a duplicate of #3418303
I believe this is related to (over even the same issue as) issue 3418303 🐛 New structure for template_preprocess_field_multiple_value_form Needs work
Default presentation attributes (fill/stroke/etc) should be moved off from SVG tags if it reduces the weight, and placed as Internal CSS
Please don't do this, especially not in core. I even propose to avoid CSS styles and encourage the use of presentation attributes, as they are very easy to overrule in CSS by contrib+custom modules and themes. To avoid repetition and weight, better use an intermediate group (e.g. <g fill="currentcolor">
) instead.
Do not re-enter the dark age of CSS selector specifity battles
.my-custom-admin-sub-theme .gin-theme #contrib-module a svg path.something { color: blue !important; }
.my-custom-admin-sub-theme .gin-theme #contrib-module a:hover svg path.something { color: red !!!!!!!!!important; }
Did you enable the filter in the text filter settings of CKeditor?
/admin/config/content/formats
Wow, kudos to @chOdec for finding this bug.
This bug is a worst-case scenario for DX, because it introduces a random and non-deterministic behavior in template and hook processing.