Leaderboard
Popular Content
Showing content with the highest reputation on 05/31/2025 in all areas
-
I would like to echo the request to get back proper checkboxes. ProcessWire provides distinct inputfields for checkboxes vs. toggles, so I'm confused to find that all of them now look like toggles. It's rather unintuitive and, as Bernhard mentioned, not amazing for module developers. Not sure what problem the global switch to toggles was solving, but I don't think there was a problem to begin with. Checkboxes are great, toggles are great, let's keep them separate 🙂6 points
-
There have been several requests and suggestions for the new AdminThemeUikit default theme. Some of them will no doubt find their way into the core CSS files, but I thought this week it would be useful to look at just how easy it is to apply some of these yourself using the custom CSS options in the AdminThemeUikit default theme. I haven't yet run these through Diogo and Jan, so there may very well be even simpler ways to do these things, but I took my best shot at answering some of the requests here. You can specify a custom CSS file (like /site/templates/styles/admin-tweaks.css), or if you are in advanced mode ($config->advanced = true) you can specify custom CSS rules directly in the AdminThemeUikit module settings. Let’s look at how to accomplish some recently requested tweaks. Also attached is a CSS file that compiles them all in one. And all of these work in both light and dark mode, by the way. CSS variables make much of this very easy to do. In this case, we’ll choose colors that are relatively close to black and white, but a little less harsh for those that like to have their screen brightness high. Of course, adjust the colors as needed for your preferences: :root { /* use slightly lighter main background color */ --main-background: light-dark(#efefef, #222); /* also slightly lighter background color for blocks like Inputfields */ --blocks-background: light-dark(#fcfcfc, #111); /* use text color that is not 100% white/black */ --text-color: light-dark(#222, #efefef); } You can adjust the button-background CSS variable: .light-theme { /* if you prefer not to have black buttons on light theme... */ /* use main color for button color */ --button-background: var(--main-color); } .pw .ui-button.ui-state-hover { /* highlight button when hovered */ filter: brightness(1.08); } Set .uk-select elements to use the inputs-background CSS variable: .Inputfields .Inputfield select.uk-select { /* make <select> elements have same background color as text inputs */ background-color: var(--inputs-background); } Here we'll use the main color as the background color for the PageList action links, and then make their text white. This gives more of an Original theme appearance, for those that prefer it: .PageList .PageListItem .PageListActions > li > a { /* make PageList items use action links that look like buttons */ display: inline-block; line-height: 1.3; background-color: var(--main-color); color: #fff; padding: 2px 6px; font-weight: bold; text-transform: lowercase; position: relative; top: -1px; } .PageList .PageListItem .PageListActions > li > a:hover { /* highlight selected action when hovered */ filter: brightness(1.08); } #pw-content-body .PageList ul.uk-pagination > li.uk-active > a { /* optional: update active pagination to use main color */ --blocks-background: #fff; --text-color: var(--main-color); } Like with the PageList action link buttons above, we apply new background and foreground colors to these sortable item-based elements. We also make a few other small tweaks to accompany the color change: .pw .Inputfields .InputfieldPageAutocomplete li, .pw .Inputfields .InputfieldPageAutocomplete li a, .pw .Inputfields .InputfieldPageListSelectMultiple ol li, .pw .Inputfields .InputfieldAsmSelect .asmListItem, .pw .Inputfields .InputfieldAsmSelect .asmListItem.ui-state-default, .pw .Inputfields .InputfieldAsmSelect .asmListItem.ui-state-hover { /* make asmSelect/autocomplete/pageListSelect use the main-color */ --main-background: var(--main-color); --border-color: var(--main-color); --text-color: rgba(255,255,255,0.9); background-color: var(--main-background); margin-top: 0; margin-bottom: 1px !important; } .pw .Inputfields .InputfieldPageAutocomplete li:hover, .pw .Inputfields .InputfieldPageAutocomplete li:hover a, .pw .Inputfields .InputfieldPageListSelectMultiple ol li:hover, .pw .Inputfields .InputfieldAsmSelect .asmListItem.ui-state-hover { /* highlight hovered items */ filter: brightness(1.05); border-color: var(--main-color); } In this case, we'll instruct <input>, <textarea>, <select> and TinyMCE elements to show a 1px border outline using the main-color when focused: .Inputfields input:focus:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]):not(.uk-form-blank), .Inputfields textarea:focus, .pw .Inputfields .Inputfield select.uk-select:focus, select.uk-select:focus { /* have inputs show a focused state indicated by border color */ border-color: var(--main-color); } .InputfieldTinyMCE .tox-edit-area { /* setup a border in tinymce that we can highlight when focused (requires latest PW dev) */ border: 1px solid transparent; } .InputfieldTinyMCEFocused .tox-edit-area { /* make tinymce show a border (like other inputs) when focused (requires latest PW dev) */ border-color: var(--main-color); } I found that InputfieldTable worked quite well as-is, but making the <table> use a transparent background color seemed to blend in a little better: .InputfieldTable .AdminDataTable tr { /* make InputfieldTable <table> have same background color as its container */ background-color: transparent; } .InputfieldTableRowSortHandle:hover, .InputfieldTableRowDeleteLink:hover { /* make table action links highlight with main color when hovered */ color: var(--main-color); } For the headers of repeater items, we take the same approach that we did with AsmSelect, PageAutocomplete and PageListSelectMultiple items, giving them the same bolder color scheme that utilizes the main color: .pw .Inputfields .InputfieldRepeater .InputfieldContent .InputfieldRepeaterItem > .InputfieldHeader, .pw .InputfieldFileList > li > .InputfieldItemHeader { /* make InputfieldRepeater item headers use the main color */ --inputs-background: var(--main-color); --border-color: var(--main-color); --text-color: rgba(255,255,255,0.9); --muted-color: rgba(255,255,255,0.8); } .InputfieldRepeaterItem > .InputfieldHeader:hover { /* make hovered repeater header slightly highlighted */ filter: brightness(1.05); } There are two CSS variables for this. One to control the border-radius of <input> elements, and another to control the border radius of buttons: :root { --button-radius: 99999px; /* this is the default value */ --input-radius: 8px; /* default is 0 */ } This is one thing I couldn't find a simple way to do with CSS, so I went ahead and added a toggle in the original theme settings (Modules > AdminThemeUikit). You'll now see an option there to "Disable toggle-style checkboxes", should you want it. As you can see from the above examples, one of many nice things about the new default theme is that it's quite simple to tweak it for your preferences. Attached is my admin-tweaks.css example file that has all of the above, and a couple more small things too. Please reply with your own custom CSS tweaks that you like. Thanks for reading and have a great weekend! admin-tweaks.css4 points
-
https://www.youtube.com/watch?v=EoEeRWHJ8xs A checkbox is a checkbox and should not be confused with a toggle, I think. A toggle is not a skin for a checkbox. https://infyom.com/blog/user-interface-design-tips-checkbox-vs-toggle-switch/ https://uxdesign.cc/the-good-the-bad-and-the-toggle-2abc0fbbd099 Quotes: A toggle switch requires two steps: selection and execution, whereas a checkbox requires only the selection of an option, and the execution/saving action is normally required later or at a different area. Usage recommendations Do not use toggles in forms. Use checkboxes or radio buttons instead. Do not use toggles in filters or multiple selections of elements. Inhale. Use toggles for settings and changes that have an immediate effect on the UI (same applies for the segmented control). Avoid mixing toggle button groups and segmented controls. Exhale. Avoid using switches with multiple options.3 points
-
@adrian No, it is not a matter of time before the old one is no longer supported. I've said this a few times already, but the Original theme is not leaving. It is "base" theme of ProcessWire, and the one which the new Default theme depends upon. If some new feature is added, it has to be accounted for in the Original theme before the new Default one. If these themes were like PHP classes, then the Original would be like WireArray, and the new Default would be like PageArray... one inherits from and depends on the other. The new Default theme is not a separate theme the way that AdminThemeReno was a separate theme. If the Original style suits your needs better, there is no reason to transition.3 points
-
@ryan thanks for doing this work! For process modules, I want to point out that you can use variables like this color: var(--text-color, #000); This will use the light/dark color from the new theme, but default to black if the variable doesn't exist and, therefore, work on the current theme also. I tend not to agree with this. The submenus have a completely different feeling than the main menu, since they are boxed and stacked vertically and allow for a much stronger marking with the background. The size of the font is also smaller and denser, and it's readability would suffer more with a lighter color. Also, unlike the main menu, the links in the submenus are not even marked with the active class, since many of them don't even represent pages, but actions. There seems to be a strong reaction against the toggles, and some good arguments. Although I would argue that, comparing to the light switch situation (referred on a blog post linked in the other thread), our toggles on and off states are unequivocally clear because of the use of muted vs strong colors. @jploch and I really like how the toggles look like in the theme, but are not insensitive to the requests. We'll discuss possible solutions with @ryan. I personally made a point in keeping the number of css vars low. I'm totally open to tweaking them, and adding a couple more, but I will definitely not add a bunch more. The idea of these variables is to allow users very easily create a different feeling to the admin, by only changing a few colors. Having few variables and making them depend, by default, on other variables, allows you to completely change all the colors with defining only 5 or 6 variables. You are correct that changing the background also changes the button text, the reason is that, like this I know for sure, that whatever colors you chose, there will be contrast between the background of the button and the text even if you don't define the (--button-color) variable yourself. For a complete change and control of the admin theme (not only colors), I would say it's better to with the original theme or AdminStyleRock and LESS. Again, this doesn't mean that we can't tweak the variables to account for cases like in your example. There's no special reason why it's --muted-color and not --muted-text-color. In our opinion, the most important information on pages list is the the name of the the pages, and the most important action is to find the page you need to work on. The action buttons are always the same, and so, it's much easier for you to find the one you want to activate, then looking for the correct page on the list. Most of the time you will even be clicking "edit", which is the first one to the right of the page name. That's a very automated process. Also, in our case, "muted" does not mean unreadable. We want the hidden pages to be as easy to find as the non hidden pages, and easier to find than the action buttons. For us, using a softer color makes it softer to the eyes that a string of words appears from nowhere when you hover the pages in the list. Again, I'm willing to tweak the name of the variables to make them clearer, and even add a couple more variables to the list, but I wouldn't be happy to add a bunch of new variables and make the system more complex.3 points
-
Thx for adding some of my requests. +1! These toggles can be a no-go. And I think the current solution is a no-go as well. Imagine you are a module developer and you want (or need) to use checkboxes in one place. What do you do? Instruct all your clients/users of your module to set their backend to not use toggles? Easy solution? Not really... What if they had some modules installed that rely on the toggle-styled toggles? For example because they built an interface like this: This interface would break when using checkboxes! Because checkboxes don't have this left-right pointing indicator. I think there needs to be a better solution. And I think, again, that the new toggles look nice, but at what cost? I think we module authors have to have the option to control whether something is a checkbox or it is a toggle. This can't be the decision of the style that is being used. Maybe you can make the toggle-styles only apply to container-divs that have the class "toggle-style" or something? That would be a progressive enhancement. All checkboxes would remain checkboxes and if you @ryan decide that toggles look nicer on the page editor's settings tab, go for it and add the class "toggle-style" there. If I develop a module where I think toggles look nicer, I can add the class "toggle-style" too. But if nobody decided to add that class, we should get good old checkboxes. And a working user interface.3 points
-
I think this has already been reported but can't find it. There is a really bad FOUC on smaller screens - the top nav shows before being hidden for the hamburger menu. Also, the close button on the hamburger menu is broken. And another crazy css variable reuse --main-background also determines the background color when hovering over menu items in the hamburger menu. This doesn't work with the colors I am trying to work with.2 points
-
Hi everyone, Tracy is now using the brand new AdminNeo project. It's been quite a rollercoaster in the Adminer world with the apparent death of Adminer, to AdminerEvo, and then AdminerNeo, then the revival of Adminer, and now AdminNeo. I have been following both Adminer and AdminNeo and deciding which one to use and while I do worry that AdminNeo might not survive given the long history of the original Adminer (now it's alive again), the theme we were using was written by the AdminNeo developer and I just can't live with any of the Adminer themes by comparison. AdminNeo also introduces a robust external login system (instead of the hack needed by Adminer), so that's where we are now. Along the way I also added styling to page IDs so you can now tell if a page is hidden, unpublished, both, or trashed: I have also added a modal viewer for images, audio, and video, along with download functionality, along with thumbnails for images. And we also finally have a nice interface for the full Adminer Process module (Setup > Adminer) when not in Standalone mode - @Robin S - I think you might actually want to use this now 😜 Please let me know if you notice any issues or have any suggestions.2 points
-
Assuming that the field would be changed in Page Edit rather than as result of some other API code, you can validate the user input in a hook to InputfieldText::processInput. Example: $wire->addHookBefore('InputfieldText::processInput', function(HookEvent $event) { $inputfield = $event->object; $input = $event->arguments(0); $field = $inputfield->hasField; $page = $inputfield->hasPage; // For a particular field name and template name if($field && $field->name === 'text_1' && $page && $page->template == 'events') { $old_value = $page->getUnformatted('text_1'); // Return early if the old value is empty if(!$old_value) return; $new_value = $input[$inputfield->name]; // If the first three characters have changed if(substr($new_value, 0, 3) !== substr($old_value, 0, 3)) { // Show an error message $inputfield->error('You are not allowed to change the first three characters of the "Text 1" field.'); // Replace the hooked method so the new value won't be saved to the field $event->replace = true; } } });2 points
-
Yes, variables can be changed in a specific scope. That being said, I was also uncertain whether utilizing this was a good idea, or recommended. Mainly as it seems that it might be prone to cause issues at some point in the future. To demonstrate, take this for an example (even if it was not used with variables specifically in the context of this thread): .PageList .PageListItem .PageListAction > li > a:hover That is a rather specific selector. If at some point in the future we needed to add an extra level of markup between ".PageListActions" and "li", or wrap the "a" tag in a "span" tag, etc. that would break styles relying on this selector. (Yes, it's just CSS, but that would still be an undesired outcome.) This is a benefit of having more, and more specific, CSS variables as well: if there was a --page-list-actions-link-hover-brightness variable, it would feel safe to use that. At the very least it would be less likely that someone decided to remove support for it from the admin style, as they would see right away that it will likely cause issues. (And no, I am NOT suggesting that we need to go to this level with our variables; just using this as a hypothetical example!) The point I'm trying to make here is that if this is what we are suggesting and/or recommending users to do, then we should be very, very mindful of making any structural changes in the admin. This may already be the case, so perhaps I'm just stating the obvious 🙂 If not making changes to structures in the admin seems unreasonable, one option might be to define "safe contexts", kind of like hooks for CSS variables — i.e. add a list of IDs or classes that "will not be changed or removed, no matter what". Things like "#pw-masthead", or "#pw-masthead .pw-search-form" from Ryan's examples, or ".pw-dropdown-menu" from Diogo's reply 🤷♂️1 point
-
Ah, almost forgot about the toggles vs checkboxes. Independently of what is decided concerning the usage of toggles, our css selector is clearly too aggressive and id trying to turn, into toggles, some checkboxes that should be left alone. We’ll need to fix that. Funny, just before submitting, i noticed this 😆1 point
-
@ryan - maybe all our concerns will go away once all the bugs are sorted out. Maybe all of bernhard's modules will work without issue, maybe my issues with checkbox toggles not working in my modules with custom AJAX loaded tables will be fixed and maybe it will be possible to skin colors easily without contrast issues. Maybe then your point about them being the same theme just with different styling will be correct and this all won't matter. If that is the case, then I sincerely apologize for my frustration. I think the problem is that it's actually hard for any of us to know what are actually issues that should be reported on Github vs decisions that have been made that we just have to deal with. I know you spent some time in the first post of this thread showing how certain things can be overridden and that is helpful, but I'd love to see you or konkat try to replace the grey background with white and make the main nav have a darker color (just a simple green or blue) and get an idea for the struggles that are had when attempting this. Here's another example I have just noticed. --masthead-active-color controls the color of the active top level menu item, but it's also used for all submenu items (which as we know don't have active or inactive states, so why is it used here?). I just don't see how that makes sense. For example, I want the green nav background and I want to distinguish the active page at the top level, but now the submenu items are also white. Is there a recommended way to deal with this? I'd ideally like the submenus to not have the green background - which can obviously be down with overrides, but it just seems like skinning has become more difficult than it used to be and css vars should make it easier. Not really - I just want to be able to adjust menu text separately to the search box without needing overrides.1 point
-
I know this is the intention and I understand how the new one depends on it, but even if it remains that way indefinitely, module authors (in particular @bernhard) are already having to deal with differences between the two - will he support both or at some point give up and only support the new? At some point new modules will come along that only work with the new theme and so those of us using the old one will have unusable modules. Software changes and in my experience you need to upgrade to the latest implementation sooner than later because at some point it will bite you and the longer you leave it, the more you will need to do to fix things.1 point
-
I don't have strong feelings about checkmark style versus toggle style checkbox controls. It sounds like some people do. I'm fine with regular checkmark style being the default rather than the toggle style, but I divert to the designers. I don't understand the strong feelings because they both represent the same selected (1) or not selected (0) states. You posted a screenshot with "light" (left) and "dark" (right) labels, and that is not what we are doing. Those labels make it represent something else. That relates more to our dedicated toggle Inputfield, which has an entirely different appearance. In the physical world, the checkmark style represents something we would do with a pen, and the toggle style represents something we'd do with our hands on a machine. If I think of an airplane cockpit, there won't be any physical check boxes and marks. The same is true with literally any physical tool or machine we interact with. On my Android phone at least, there are no checkmark style inputs in the OS, they are all toggle style. So I'm quite used to them being one and the same as check marks, which is probably why I don't notice one way or the other. But I do prefer checkboxes when they are unlabeled and in the far-most right column of a table, like a delete checkbox, because it uses a little less space, even if a toggle style checkbox might be easier to click/tap vs. an unlabeled checkbox.1 point
-
Hi @diogo - thanks for your thoughts! I think the key thing here is that if you like the look of the new theme (and there are many things I do like), then it's great (checkbox/toggle issues aside), but if you don't like the look or just want to theme it easily to match a client's brand, then it's really painful to adjust - I think that is what most of us are taking issue with. The key things I want to be able to do easily are: set the background and text color of the top level nav without it impacting the color of the dropdowns (like we could before) change the grey background to white and be able to fix the issue with the background color of the page actions row - personally I would really appreciate an option to go back to the button look without css overrides, but just being able to set the background of the rows without it impacting other things (I can't remember now what it was) would be fine. change the inactive state of buttons to match the primary color rather than using it for the hover state Honestly, that would satisfy everything for me. Thanks for considering. Other than Modules > Refresh, I am curious what else are actions and not pages? I really think the active class here would be great. Sorry, but even though this sounds good in theory, I really don't think it works in practice. You can't overwrite variables that are the dependents of other variables without consequences (unless there is something I am missing). And the contrast issues prevent changing 5 or 6 variables to get things working if you decide to go with darker over lighter colors for backgrounds. The thing is though, that with AdminStyleRock you only needed to change a couple of things to make a significant change. You can't actually do that with the new approach (and have it remain readable). But, I really love CSS vars and want to make this new approach usable. The issue with page list actions comes about when you want the background to be white and not the new grey. Now the white background highlighted color of the page list actions row doesn't work so the links to "edit" aren't very visible at all. Changing the background color of these has been other consequences because of the few css vars issue. So, I ended up restoring the actions to buttons using .PageList .PageListActions a, .PageList .PageListActions a:hover,.PageList .PageListerActions a,.PageList .PageListerActions a:hover, .PageList .PageListMoveNote a {1 point
-
It's somewhat experimental (never got to use it much), but for SearchEngine there is also an add-on for indexing file contents (plain text, PDF, and most "office formats"): https://github.com/teppokoivula/SearchEngineFileIndexer. Honestly not sure how well it works at the moment 🙂1 point
-
--muted-color is also used for the page list action links but these should be highlighted, not muted. But regardless of what you think they should look like, they definitely shouldn't be tied to the same color as hidden pages. And is there a reason it's --muted-color and not --muted-text-color?1 point
-
Sorry to keep going, but we desperately need more css vars. --blocks-background controls the color of so many things it makes it effectively useless. It impacts the top nav, the actual field blocks, the color of button text (what's with that?), the page list actions hover bar, and probably more. I love css vars (I never jumped on the less/sass bandwagon), but they only work if they are distinct enough to modify everything as needed because if we need to mix and match between css vars and class/id targeted overrides it becomes an awful mess very quickly and things will surely get broken with future PW changes. I really am trying to figure out a way I can turn on the new theme for users because I know that it's only a matter of time before the old one is no longer supported (that's not a slight, it's just reality when there is limited time and resources to maintain things). I really wanted the background of the main nav to be darker color with white text, so went with: :root { --main-background: #FFF; --text-color: light-dark(#444, #efefef); --muted-color: light-dark(#999, #efefef); --masthead-text-color: #FFF; } #pw-masthead { background-color: #c3d152; } but you end up with the placeholder for the search box white as well. I am sure I can override that as well but my point is that it gets messy fast without more specific vars to work with.1 point
-
Following on from @Tiberium's suggestion, which is how I've done such things myself in the past, if you want to extract text for search out of just about anything there's Apache Tika. It's in Java so, again as suggested, you'd need some sort of VPS or dedicated server – or perhaps you could make it part of a pipeline (e.g. on a local machine) to deliver content to your site.1 point
-
Plates for ProcessWire is a module to make using Plates with your ProcessWire templates plug-and-play. Plates is an extremely lightweight pure PHP templating system that provides features that developers have come to expect when building applications and sites in ProcessWire and beyond. From the Plates website: Highlights from the documentation: Native PHP templates, no new syntax to learn Plates is a template system, not a template language Plates encourages the use of existing PHP functions Increased code reuse with template layouts and inheritance Template folders for grouping templates into namespaces Data sharing across templates Pre-assign data to specific templates Built-in escaping helpers Easy to extend using functions and extensions Plates is an extremely stable application that has been in development and use in production since 2014. This module is also a simple adapter so I am confident in it's stability as I've already used it myself. However, the custom extensions included should be considered early releases and bug reports are welcome, pull requests are very welcome as well! If you're familiar with Plates or just want to get started now, you can download the module from the Github repository. Batteries are included, documentation provided. Previously this module requires that you install Plates manually via Composer. To make the module more accessible and a true "drop-in and code" solution, Plates is now included. If you prefer to manage your packages separately via Composer, or have already installed this module and Plates, Plates for ProcessWire will use the version you have installed then fall back to the version shipped with the module if it is not present. So, FireWire, why another templating engine? There are many stellar templating engines available. I've used several of them and they have truly great features. I also appreciate the simplicity of working with PHP. While templating engines do sometimes offer more terse syntax, it's not a killer feature for everyone like code reuse, nesting, and layouts may be. Code editors will always offer first-class support for PHP and syntax highlighting makes some of the arguments about readability less of a feature benefit for alternatives. Plates takes care of the limitations that come with writing pure PHP templates. Plates feels very at home with the ProcessWire API. Like ProcessWire, it also scales infinitely and its focus on core features over the large library approach makes for a great developer experience. If you've worked with templating engines in the past, the features are familiar. If you haven't, you'll be up to speed remarkably fast. I wrote this module with the intention of making it a "drop-in and code" experience, and I've worked on using the extensibility of Plates to add some extra superpowers to boot. Plates is another great option that may be useful to you whether because it's more your style, it fits your use case, or maybe your first time adding a little extra oomph to your ProcessWire templates. The first 10 minutes you spend with the Plates documentation might be the last 10 minutes. A Simple Example Start with files and folders. Things to know off the bat: Plates for ProcessWire comes pre-configured to look for Plates templates in your /site/templates folder By default it will look for files with the extension '.plates.php' to help differentiate from ProcessWire files, however this may be customized to any extension you prefer on the module config page The folder structure here is entirely up to you, this example can be used but is not required. /site /templates /components image_gallery.plates.php /layouts main.plates.php /views home.plates.php home.php ready.php Your ProcessWire templates will contain one line that hands off rendering to Plates <!-- /site/templates/home.php --> <?=$plates->templates->render('views/home')?> Start by creating your layout. We'll keep it simple. <?php namespace ProcessWire; // /site/templates/layouts/main.plates.php /** * @property string|null $title Page title * @property string|null $description Meta description */ ?> <!DOCTYPE html> <html> <head> <title><?= $title ?? $page->title; ?></title> <?php if ($description ?? null): ?> <meta name="description" content="<?=$description?>"> <?php endif ?> <link rel="stylesheet" href="<?=$config->paths->templates?>styles/app.css"> </head> <body> <header class> <img src="/path/to/logo.jpg"> <nav> <ul> <?php foreach ($navBase->children->prepend($pages->get('/')) as $navPage): ?> <li> <a href="<?=$navPage->url?>"><?=$navPage->title?></a> </li> <?php endforeach ?> </ul> </nav> </header> <section> <?= $this->section('hero'); ?> </section> <?= $this->section('content') ?> <footer> <?= $this->section('footer'); ?> </footer> <script src="/path/to/your/file.js"></script> </body> </html> I like to add docblocks at the top of my Plates templates because we can pass data to any template or layout wherever needed and this helps understand what is accepted or expected at a glance. This is optional and just a style preference. Some notes: The full ProcessWire API is available including language functions Your Plates templates are rendered inside a Plates Template object. To use any Plates function, custom function, or custom extension you use $this Jumping over to home.plates.php <?php namespace ProcessWire; // /site/templates/views/home.plates.php $this->layout('layouts/main', ['description' => $page->description]); ?> <?php $this->start('hero') ?> <h1><?=$page->headline?></h1> <img src="<?=$page->hero_image->url?>" alt="$page->hero_image->description"> <?php $this->end() ?> <section> some stuff here </section> <?php if ($page->gallery->count()): ?> <section> <?php $this->insert('components/image_gallery', [ 'images' => $page->gallery, 'title' => __('Image Gallery'), ]) ?> </section> <?php endif ?> <section> Some stuff there </section> <?php $this->start('footer') ?> <p>Thanks for visiting</p> <?php $this->end() ?> Things to note: Even though this file is located in the 'views' subdirectory, Plates is configured out of the box to use '/site/templates/' as the base directory, so you can write paths without '../' directory traversal We chose the main layout and passed the 'description' variable which is available in main.plates.php as $description $this->start('section name') and $this->stop() capture what is output to those sections in main.plates.php, there is no limit on sections and they can have any name aside from 'content' which is reserved. Any content that exists outside of a defined start/stop section is automatically output to the 'content' section in your layout And the image gallery: <?php namespace ProcessWire; // /site/templates/components/image_gallery.plates.php /** * @property string|null $title Optional gallery title * @property Pageimages $images Images field */ ?> <div> <?php if ($title ?? null): ?> <h3><?=$this->batch($title, 'strtolower|ucfirst')?></h3> <?php endif ?> <ul> <?php foreach ($images as $image): ?> <li> <img src="<?=$image->url?>" alt="<?=$image->description?>"> </li> <?php endforeach ?> </ul> </div> Additional notes: You can use $this->insert() in any Plates file, including layouts. You can also use $this->insert() to nest Plates templates in other Plates templates You can use batch() to execute multiple functions on a value. Any PHP function that accepts one argument (or one argument and the rest optional) can be chained in a batch. This also works with custom functions and Extension functions where you can do some really neat stuff. This is similar to functions and filters in other templating engines. The Syntax The syntax, like ProcessWire, is just PHP and has complementary style and a simple API. Plates only makes style recommendations. One of the key elements to making templates in any engine work is knowing where to put logic and where control structures should do the heavy lifting. With PageClasses and organized code, templates can be clean and concise with just PHP variables, loops, and control structures. At it's core, Plates primarily keeps focus on templates which where other engines that tend to include new syntax and tools because they already have to build a parser or interpreter. The batch() function covers a most use cases and is a welcome tool to use as is or as a complement to more custom functions and extensions. That's all you need to get started using Plates for ProcessWire. I highly recommend reviewing the short documentation to get the most out of templates in your projects. Layouts - A core templating feature for sharing page designs and base code between templates Nesting - Enhanced code reusability by inserting code blocks Inheritance - Use code sharing between templates to build more complex designs with simplicity Functions - Batching functions and writing your own Plates for ProcessWire comes with several custom build extensions for this module that may be useful. All extensions are optional and disabled by default. You can start building with the core Plates system. Extras: Plates for ProcessWire Extensions (optional) NOTE: All examples below are part of custom extensions written for this module. They can be enabled or disabled on the Plates for ProcessWire module config page, all are disabled by default. These custom extensions have full documentation that is accessible on the module config page or by reading the markdown documents directly in the module directory. This module comes with several extensions that add useful tools for building templates. Many also provide some parity with other templating solutions. Plates for ProcessWire extensions provide over 100 custom functions to use in your templates. Many of them are batchable, many of them are written to use with ProcessWire objects such as Page and WireArray/PageArray. Others are intended to make template code shorter and cleaner, others still are just nice to have. The Conditionals Extension brings some efficient output options. <!-- From our example above. Instead of this --> <?php if ($page->gallery->count()): ?> <?php $this->insert('components/image_gallery', [ 'images' => $page->gallery, 'title' => __('Image Gallery'), ]) ?> <?php endif ?> <!-- Consider this --> <?php $this->insertIf('components/image_gallery', $page->gallery->count(), [ 'images' => $page->gallery, 'title' => __('Image Gallery'), ]) ?> Tidy up single line outputs <!-- Instead of this --> <?php if ($page->text_field): ?> <h2><?=$page->text_field?></h2> <?php endif ?> <!-- Consider this. The if function accepts two argument and outputs the second if the first is truthy/falsey --> <?=$this->if($page->text_field, "<h2>{$page->text_field}</h2>")?> Conditionals also provide cleaner syntax for some control flow operations <!-- Instead of this --> <h2> <?php if ($weather === 'sunny'): ?> <?=__('Grab your sunglasses')?> <?php elseif ($weather === 'cold'): ?> <?=__('Wear a coat')?> <?php elseif ($weather === 'cold'): ?> <?=__('Bring an umbrella')?> <?php endif ?> </h2> <!-- Consider this --> <h2> <?=$this->match($weather, [ 'sunny' => __('Grab your sunglasses'), 'cold' => __('Wear a coat'), 'rainy' => __('Bring an umbrella'), ])?> </h2> <!-- When more complex evaluations are needed, consider matchTrue --> <h2> <?=$this->matchTrue([ __('Tickets are available') => $ticketCount > 10, __('Hurry, tickets are almost sold out') => $ticketCount > 1, __('Sold Out') => true, ])?> </h2> The Functions Extension provides a wide array of flexible and batchable functions <!-- Get the sum of all items in a WireArray/PageArray, associative array, or object by field/key/property, Also works on indexed arrays --> <p>Total: <?=$this->sum($page->cart_items, 'price')?></p> <!-- Group items in an associative array, array of objects, or WireArray/PageArray by property or field --> <?php foreach ($this->group($pages->get('template=players'), 'team_name')) as $teamName => $players): ?> <h2><?=$teamName?></h2> <ul> <?php foreach ($players as $player): ?> <li> <?=$player->title?><br> <?=$player->position?> </li> <?php endforeach ?> </ul> <?php endforeach ?> <!-- Get PageArrays inclusive of their parent using withChildren() Assign attributes/values if a page matches the current page using attrIfPage() --> <nav> <ul> <?php foreach ($this->withChildren('/') as $navItem): ?> <li<?=$this->attrIfPage($navItem, 'class', 'active')?>> <a href="<?=$navItem->url?>"> <?=$navItem->title?> </a> </li> <?php endforeach ?> </ul> </nav> <!-- A second argument is a selector for child pages --> <?php foreach ($this->withChildren('/', 'template=team_members') as $navItem): ?> <!-- Generate an unordered list of breadcrumbs --> <?=$this->breadcrumbs(['startPage' => '/', 'separator' => ' | ', 'ulClass' => 'breadcrumb-nav'])?> <!-- Create an indexed array with iterator from index 1 on any iterable object --> <?php foreach ($this->batch($page->images, 'toList|from1') as $i => $image): ?> <img src="<?=$image->url?>" alt="<?=$image->description?>" data-slide-index="<?=$i?>"> <?php endforeach ?> The configurable Asset Loader extension lets you link, inline, and preload assets with automatic cache busting version parameters. Directories and namespaces are customizable on the module config page and are yours to choose. <?=$this->preloadAssets([ 'fonts::ProximaNova.woff2', 'fonts::ProximaNovaLight.woff2', 'js::app.js', ])?> <?=$this->linkAsset('styles::app.css')?> <?=$this->inlineAsset('styles::critical.css')?> <?=$this->linkAsset('js::app.js')?> There are more extensions and powerful utilities. If you're a RockPageBuilder rockstar, check out the README file for details on how to use an included utility function to make Plates and RPB work together 👍 Try It Out! If you want to give it a try, download the module from the Github repository and take it for a spin. When it gets a little more testing I may submit it to the modules directory. I'm a consistent user of plain PHP, Latte, and Blade for templating and I think Plates is a great addition to the developer toolbox. Interested to hear your thoughts.1 point
-
@zilli Some great questions! I spent many years writing vanilla PHP template markup. For Occam's Razor, have the leeway to apply as many or few features a tool offers. Your mentioning Markup Regions is a good example, I use many features of ProcessWire but have never used Markup Regions (I have no opinion, it just coincidentally never became part of my workflow). Both MR and templating engines aim to overcome challenges introduced when using PHP (like any language) alone for output. So it's up to the preference of the developer and needs of a project, a la "there are no wrong answers". My limited experience with MR make me a less than ideal candidate to draw comparisons or speak to compatibility. My first thought it to flip the question of complexity and see how it applies to tools you/me/we may already use. Page Classes are a layer of complexity to abstract logic out of presentation, ProcessWire itself is a layer of complexity to abstract database transactions/data management. Templating strategies are a layer of complexity that make your workflow less complex. Tools like MR and templating packages do that as well while sometimes affording some extra tools. My experience with templating solutions was born out of bumping my head on limitations and moments of thinking "there has to be a better way to do this". I ended up using creative tricks to make 'require' and 'require_once' carry the load but ended up making my code harder to manage, more files to make it work, and felt like I was breaking good practice rules. Consider this simple example: <!-- /site/templates/inc/header.inc.php --> <!DOCTYPE html> <html lang="en"> <head> <title><?=$page->title?></title> <?php if ($includeGoogleAnalytics ?? false): ?> <?php require_once '/path/to/google_analytics.inc.php' ?> <!-- GA code here --> <?php endif ?> </head> <body id="$pageId" class="<?=$pageClass?>"> <header> <img src="/path/to/logo.png"> <h1><?=$headline?></h1> </header> Then I have my home.php and to make this work, I have to start setting big scope variables to be made available to the 'require_once' code. This got worse as the site needed more features and the header and footer became more complex. Later I had to create a new headers for other parts of the site, so multiply the code above a few more times, and add some more 'require_once' lines to each of them. <?php namespace ProcessWire; // /site/templates/home.php $pageId = 'stuff-here'; $pageClass = 'stuff-there'; $includeGoogleAnalytics = true; require_once __DIR__ . 'partials/header.inc.php'; ?> <!-- We haven't even added one line of markup/code for the home page --> <?php $includeCallToAction = true; $includeTestimonials = false; $anotherOne = true; $iHearYouLikeOutOfScopeVariables = false; require_once __DIR__ . 'partials/footer.inc.php'; ?> Here's an actual snippet from a site I build years ago. If you look at a partial or component and it has a lot of if statements, sometimes but not always, it says "I'm a file that does too much" and it becomes increasingly difficult to manage. <!-- Real snippet of production code from /site/templates/partials/footer.inc.php --> <?php if ($mainNav): ?> <?php require_once __DIR__ . '/../inc/site_nav_overlay.inc.php' ?> <?php endif ?> <?php if ($salesCtaModal): ?> <?php require_once __DIR__ . '/../inc/cta_sales_quote_modal.inc.php' ?> <?php endif ?> <?php if ($solarCleaningCtaModal): ?> <?php require_once __DIR__ . '/../inc/cta_solar_cleaning_modal.inc.php' ?> <?php endif ?> <?php if ($solarRepairCtaModal): ?> <?php require_once __DIR__ . '/../inc/cta_solar_repair_modal.inc.php' ?> <?php endif ?> Is this how everyone does it? Maybe not, hopefully not, but every project runs the risk of becoming Frankenstein's monster. With a templating solution approach this would have been solved with 4 files. You can significantly reduce the number of if statements because each of these have a job to do, they know what it is, and when you're maintaining the site, or adding new features, there is zero confusion about where to go. Working on the blog parts? Go edit the blog templates and blog layout. The service/maintenance pages know they'll need the "cleaning" and "solar repair" modals so that whole if block doesn't exist anymore. Cake. <?php namespace ProcessWire; // This is the base layout. The layouts below declare this as *their* layout and pass data if needed $this->layout('layouts/base'); // For the majority of pages on the site $this->layout('layouts/main'); // For the pages dedicated to customer service and maintenance rather than sales $this->layout('layouts/service_mainenance'); // For all blog and blog related pages $this->layout('layouts/blog'); ?> Long story short, the complexity would have been immediately reduced by introducing a templating tool with a predictable set of features and functions to use. To contrast, as a longtime user of preprocessors myself, I can say that those have the potential to introduce a lot of things that fundamentally change how you work with the language itself. Sass/Less bring nesting, loops, variables (before custom properties were available), mixins, includes, file imports, modifier functions, if/else control flows, custom functions, (the list goes on) and an entire JavaScript toolset to handle it. You can solve a lot of problems you don't have! This isn't an argument against preprocessors, but there is a higher level of discipline you must have to keep this from going off the rails and being so clever you outsmart yourself. On the other hand, templating is so incredibly fundamental to producing apps and websites that the constrains start to tighten much sooner and it becomes more clear how many workarounds and "bending the rules" of good practice are present. The benefits of introducing templating tools bring a higher number of benefits that are more impactful faster. You're also adopting a common development practice, so it is leveling up your skillset. I have to mention that these benefits are not unique to Plates. Layouts, and enhanced insert/include features, are common to pretty much all templating tools. Pick the one that feels right be it Latte, Twig, Smarty, or Plates. My only argument in favor of Plates is that if templating is something that you're introducing into your projects for the first time you may be up to speed faster because: a) the core features set of Plates is very limited (intentionally), and b) it's the same PHP syntax you already use. The concepts and strategy you use with Plates will translate to other templating tools. In the case of this module, the potential amount of complexity is almost entirely due to the custom extensions I built which is why they are optional and disabled by default. You can safely ignore everything in my post above from "Plates for ProcessWIre Extensions" on down and still get simple yet powerful tools. If you have any Q's, post them here.1 point
-
@adrian I'm still learning about CSS properties/variables, so I might be missing something. But based on what you've said above (and in other places), I'm wondering if you are missing the cascading and inheriting aspects of CSS properties/vars? Maybe you are thinking of them like PHP variables? They are quite a bit more powerful than that. If I understand it correctly, the actual number of variables you can customize is the number of CSS variables multiplied by the number of elements using it that can be targeted with a selector. We probably have thousands of customizable properties just with the set of variables we have now, where the selector is like the namespace for the variable. So when you set a value of something like --blocks-background, you are setting a default/fallback value if setting it within :root { ... }, and then, it may be overridden further down the tree. In your masthead example, you would target the --blocks-background within #pw-masthead, or something more specific within it. In your PageList example, you would target --muted-color within .PageList, or something more specific within it. So in my mind, it doesn't make sense to have tons of CSS variables unless you plan to treat them like PHP variables, which would be using them incorrectly. The current set of CSS variables seems well thought out to me, but I'm still learning.0 points