Jump to content

RockFrontend Site Profile - RockFrontend + UIkit (optional) + TailwindCSS (optional)


Recommended Posts

The RockFrontend site profile for ProcessWire offers a unique combination of benefits by integrating UIKit and TailwindCSS, making it an appealing choice for developers looking for both robust UI components and extensive customization capabilities. Here are some key advantages:

  1. Rich UI Components from UIKit
    UIKit is known for its comprehensive collection of high-quality, ready-to-use UI components. This integration in the RockFrontend site profile allows developers to quickly implement complex components like modals, accordions, and sliders that are visually appealing and functionally robust.
     
  2. Flexibility and Customization with TailwindCSS
    TailwindCSS is a utility-first CSS framework that provides high granularity in styling elements. It allows developers to build custom designs without stepping out of the framework’s constraints. This can significantly speed up the development process, as it eliminates the need to write custom CSS from scratch.

Installed Modules

Github: https://github.com/baumrock/site-rockfrontend

Modules Directory: https://processwire.com/modules/site-rock-frontend/

  • Like 5
Link to comment
Share on other sites

  • 2 weeks later...

Hello Bernhard,

basic-page.php and home.php are empty template files.
Are they only needed (even empty) if custom page class(es) php files that are related to them exist?
Because latte files are used here I'm a bit confused about the (possible) role of these template files.

In this site profile, in which use case(s) would they not stay empty?
Could you please give one (or more) example(s)?

Thanks in advance!

NB: I don't want to use tailwind, but I'm curious about _tailwind.css, I think it's the first time I see the "_" prefix on a css file name.

 

  • Like 1
Link to comment
Share on other sites

Thx @Christophe that's some good questions 🙂 

I've updated the profile to hopefully answer all of them: https://github.com/baumrock/site-rockfrontend/commit/be55adae28ffe6a7aac83944284820f17afdaabf

18 hours ago, Christophe said:

NB: I don't want to use tailwind (for several reasons, one of them is that there are resets used but uikit uses normalize already, doesn't it? (I would perhaps prefer modern-normalize or another "at-rest" for the moment project related to one of the css grids evengelists (I don't remember her name right now)), BUT I'm curious about _tailwind.css, I think it's the first time I see the "_" prefix on a css file name.

You don't need to use tailwind. If you don't want to use it just remove the _tailwind.css file. If you find anything happening that should not be let me know and I'll fix it.

  • Like 1
Link to comment
Share on other sites

Hello Bernhard,

A bit of context before the questions below in the message.

I want to display a list of children of the parent pages that are using the template "parent-page".
So, I have created parent-page.php (empty for the moment) in /site/templates.
And ParentPagePage.php in /site/classes in which I have this (where I tested different things):

<?php

namespace ProcessWire;
 
class ParentPagePage extends Page
{
public function listchildren()
{
return "Clickable list of children";
}
}

I had more elaborate code(s) in the return statement, but things weren't working so I just put a string in it to keep it simple.
(I've just noticed that vscode and intelephense are writing "Undefined type 'ProcessWire\Page'." in the PROBLEMS tab.)

So I had :

  • Method DefaultPage::listchildren does not exist or is not callable in this context on pages that do not use the parent-page template (and use the basic-page template).
  • Method HomePage::listchildren does not exist or is not callable in this context on the homepage.

For the error messages and blank page(s) to disappear it seems that it is mandatory to use this in main.latte:

{if $page->template == 'parent-page'}
{$page->listchildren()}
{/if}

and not just {$page->listchildren()}.

At one point with custom page classes I think I was hoping it would also remove the need for that if condition.

In /site/templates/sections/main.latte I currently have the code that follows - at the end of the message now (we apparently can't escape having to use the |noescape filter).
I simplified the code to only define and check $image (the code borrowed from another website was also defining and checking $images, so not only the first image, I don't remember why/if it was needed).

SO, my question(s) is how could this code be simplified, in part in order to have cleaner code in this main.latte file?
Which parts of the code should be in main.latte, which ones in ParentPagePage.php and which ones perhaps in parent-page.php?
How would you organize/separate them in the different files?
Also, can $image and $thumb be defined elsewhere? Etc.

Thanks again in advance!

I tried yesterday with simpler children list codes (without images and uikit markup), but it seemed complicated and not clean.
And now it's more complicated.
I've tried with code directly inside the return statement, and also with a variable and then returning the variable.

$thumb is currently defined if $image exists. If I used <img n:if="..." src="... it would be changed. But perhaps it wouldn't be important if it wasn't defined inside the {if}...{/if}.

<main>
<div class="uk-container">
<h1>{$page->title}</h1>
{$page->body|noescape}

{if $page->template == 'parent-page'}
{$page->listchildren()}
{/if} 

<div n:if="$page->template == 'parent-page'" class='uk-child-width-1-3@m uk-grid-match uk-margin-small-top uk-margin-medium-bottom uk-text-center' uk-grid>
<div n:foreach="$page->children() as $child">
<div class='uk-card uk-card-small uk-card-default uk-card-body'>
<a class="h2" href="{$child->url}"> 
{var $image = $child->images->first()}
{if $image}
{var $thumb = $image->size(200, 150)}
<img src="{$thumb->url}" width="{$thumb->width}" height="{$thumb->height}" alt="{$image->description}" />
{/if}
<h2>{$child->title|noescape}</h2>
</a>
</div>
</div>
</div>
  
</div>
</main>
Edited by Christophe
Problems with the display of code. Still tabulation issue.
Link to comment
Share on other sites

Hi @Christophe

7 minutes ago, Christophe said:

(I've just noticed that vscode and intelephense are writing "Undefined type 'ProcessWire\Page'." in the PROBLEMS tab.)

Unfortunately we don't have Code Intellisense or autocomplete in latte files at the moment 😞 See https://github.com/smuuf/vscode-latte-lang/issues/5

9 minutes ago, Christophe said:

So I had :

  • Method DefaultPage::listchildren does not exist or is not callable in this context on pages that do not use the parent-page template (and use the basic-page template).
  • Method HomePage::listchildren does not exist or is not callable in this context on the homepage.

You are adding the method "listchildren()" to your ParentPagePage, nowhere else. That means you can all this method only on pages that have the template "parent-page".

11 minutes ago, Christophe said:

At one point with custom page classes I think I was hoping it would also remove the need for that if condition.

Why should it do that? You are calling a method of an object. If that does not exist it is expected to throw an error. You CAN do what you describe though. Sometimes it is useful and I'm doing that as well. You could add that method to your DefaultPage.php and there return an empty sting. That would make $page->listchildren() return "" by default, which means it does not throw an error. For some page types you can then implement a different logic:

DefaultPage.php                   --> listchildren() { return ""; }
FooPage.php extends DefaultPage   --> listchildren() { return "foo!"; }
BarPage.php extends DefaultPage   --> listchildren() { return "BAR"; }

// in any template
echo $page->listchildren();

 

16 minutes ago, Christophe said:

SO, my question(s) is how could this code be simplified, in part in order to have cleaner code in this main.latte file?
Which parts of the code should be in main.latte, which ones in ParentPagePage.php and which ones perhaps in parent-page.php?
How would you organize/separate them in the different files?
Also, can $image and $thumb be defined elsewhere? Etc.

Thanks again in advance!

That it totally up to you. Same with regular ProcessWire templates. It's up to us devs how we organise things and everybody does it differently.

I can only say what I came up with and what works great for all kings of projects (from very simple to very complex ones):

  • layout.latte for main markup
  • /site/templates/sections/... for all sections
  • /site/templates/partials/... for all partials

Details:

layout.latte --> holds the html markup skeleton of the page, eg:

<html>
  <head>...</head>
  <body>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </body>
</html>

Then all my template files are usually empty (like home.php, basic-page.php, foo.php, bar.php).

Next, I divide my design into sections. A section is a portion of the page that goes from the very left to the very right and has an undefined height/content.

You can then add those sections to your main markup file like this:

<html>
  <head>...</head>
  <body>
    <header>
      {include "sections/topbar.latte"}
      {include "sections/navbar.latte"}
    </header>
    <main>
      {include "sections/breadcrumbs.latte"}
      {include "sections/content.latte"}
      {include "sections/content-footer.latte"}
    </main>
    <footer>
      {include "sections/footer.latte"}
    </footer>
  </body>
</html>

This is often all I need, because I build all the available sections in RockPageBuilder and then I can simply add them to the pages as I need them. But it works without RockPageBuilder as well. You then just need to code all your page layouts depending on the page template. If all those pages share the same breadcrumbs and content-footer it could look like this:

<html>
  <head>...</head>
  <body>
    <header>
      {include "sections/topbar.latte"}
      {include "sections/navbar.latte"}
    </header>
    <main>
      {include "sections/breadcrumbs.latte"}
      {if $page->template == 'foo'}
        {include "sections/content-foo.latte"}
      {elseif $page->template == 'bar'}
        {include "sections/content-bar.latte"}
      {else}
        {include "sections/content.latte"}
      {/if}
      {include "sections/content-footer.latte"}
    </main>
    <footer>
      {include "sections/footer.latte"}
    </footer>
  </body>
</html>

You could also use a dynamic approach:

<html>
  <head>...</head>
  <body>
    <header>
      {include "sections/topbar.latte"}
      {include "sections/navbar.latte"}
    </header>
    <main>
      {include "sections/breadcrumbs.latte"}
      {include "content/" . $page->template . ".latte"}
      {include "sections/content-footer.latte"}
    </main>
    <footer>
      {include "sections/footer.latte"}
    </footer>
  </body>
</html>

It's just regular PHP inside the latte tags, so you could also do is_file() checks etc:

<html>
  <head>...</head>
  <body>
    <header>
      {include "sections/topbar.latte"}
      {include "sections/navbar.latte"}
    </header>
    <main>
      {include "sections/breadcrumbs.latte"}
      
      {var $file = "content/" . $page->template . ".latte"}
      {if is_file($file)}{include $file}{/if}
      
      {include "sections/content-footer.latte"}
    </main>
    <footer>
      {include "sections/footer.latte"}
    </footer>
  </body>
</html>

Obviously you'd need to create those files. Here I'm placing them into a new folder called "content", which would be /site/templates/content/foo.latte for example.

Next, partials:

If you have repeating markup inside your sections you can outsource them into the partials folder. For example let's say you want to show a blog overview page that shows all single blog items as cards.

You'd have a section like "blog-overview.latte" like this:

// blog-overview.latte
<div uk-grid>
  <div n:foreach="$page->children() as $item">
    <div class="uk-card">
      <h3>{$item->title}</h3>
      <p>{$item->teaser()}</p>
    </div>
  </div>
</div>

This example is very simple and if you only use your cards here, then it's easier to leave them here. If you are using the exact same cards on two or three places then you can create partials for them:

// blog-overview.latte
<div uk-grid>
  {foreach $page->children() as $item}
    {include "partials/card.latte", item: $item}
  {/foreach}
</div>

// /site/templates/partials/card.latte
<div>
  <div class="uk-card">
    <h3>{$item->title}</h3>
    <p>{$item->teaser()}</p>
  </div>
</div>
  • Like 2
Link to comment
Share on other sites

  • bernhard changed the title to RockFrontend Site Profile - RockFrontend + UIkit (optional) + TailwindCSS (optional)
  • 2 weeks later...
On 6/3/2024 at 9:44 AM, bernhard said:

Update: The module is now available in the modules directory 🙂 

@bernhard, this module uses both UIKit and Tailwind in conjunction? The use of UIKit is because it has premade components not available in Tailwind (out of the box)? If in conjunction, won’t the CSS styles clash with each other? My preference is for Tailwind styling so for the components we are beholden to UIKit-way of styling?

Link to comment
Share on other sites

7 minutes ago, HMCB said:

module uses both UIKit and Tailwind in conjunction?

Yes!

7 minutes ago, HMCB said:

The use of UIKit is because it has premade components not available in Tailwind (out of the box)?

Yes. All the great things that UIkit has to offer: Accordions, Tooltips, Alerts, Offcanvas, Dropdowns etc etc

8 minutes ago, HMCB said:

If in conjunction, won’t the CSS styles clash with each other?

Some parts do unfortunately, but only very, very little, because the profile only uses the @base directives from Tailwind and obviously also the @utilities (for all the great utility classes).

@Stefanowitsch found out that we can even turn off preflight in the base component an we tried that on a recent project - he might have some more information on that topic.

11 minutes ago, HMCB said:

My preference is for Tailwind styling so for the components we are beholden to UIKit-way of styling?

I'm not sure what you mean here, but you can do whatever you want or prefer. I'm using UIkit components and then add Tailwind for all the individual changes that I need, that are not global.

So if I wanted to change the global font-size I'd adjust the UIkit LESS/SCSS variable.
If I wanted to change the global "margin-top": Same.

But for all the small little changes that would prior go into custom .less files I just add tailwind utility classes.

Think of it as UIkit on Steroids 😎

  • Like 1
Link to comment
Share on other sites

Just because I had this example right now I thought it's a nice real world example why I really like the UIkit + Tailwind combo! I'm building this UI for a webshop where you can choose the taxrate:

emZEprR.png

First I tried to use a <select> but I wanted to show more info in the dropdown and less in the spot where the arrow points to, so I went with a UIkit Drop.

Such a dropdown sounds easy to build with some alpine magic at first, but doing it right is quite some effort! For example you have to think about accessibility and add support for keyboard navigation. UIkit has you covered.

Another nice thing to mention is that the dropdown shows taxrates first and then the description on the right. This was extremely easy thx to tailwind:

<span class='w-8 inline-block'>
  {$taxrate->taxrate()}%
</span>
{$taxrate->title}

So all I had to do was to make the element inline-block and add a dedicated with (here w-8). That leads to a better table-like rendering 🙂 

And finally, UIkit power again:

22DtiHP.png

Consider the taxrate element has been hovered/clicked on mobile being at the very bottom of the page. There is not enough room for the dropdown, so UIkit will display it on top of the element automatically 🚀

I wouldn't want to implement that on my own with only TailwindCSS + AlpineJS. Both tools are great, but I also find it great to have proven components from UIkit that I can use and adjust to my needs very quickly.

PS: Here is the final snippet that combines all the great tools Latte + UIkit + TailwindCSS and is fully reactive thx to AlpineJS 😍

<div class='text-xs'>
  Preis
  <div
    class="uk-inline"
    {$rockcommerce->taxselect->attrs()|noescape}
  >
    <span class='cursor-pointer underline'>
      inkl. <span x-text='taxrate'></span>% MwSt.
    </span>
    <div class='uk-card uk-card-default uk-width-auto p-3' uk-drop='offset:10'>
      <div
        n:foreach='$rockcommerce->taxrates() as $taxrate'
        class='uk-text-nowrap'
        data-text='inkl. {$taxrate->taxrate()}% MwSt.'
      >
        <label>
          <input
            class="uk-radio mr-1"
            type="radio"
            name="taxrate"
            value="{$taxrate->taxrate()}"
            x-model='taxrate'
          >
          <span class='w-8 inline-block'>
            {$taxrate->taxrate()}%
          </span>
          {$taxrate->title}
        </label>
      </div>
    </div>
  </div>
  zzgl. Versandkosten.
  <br>
  Verfügbarkeit: Auf Anfrage.
</div>

Oh... There's another element with some TailwindCSS magic: span with class cursor-pointer underline that makes it look like a link so that the user knows he/she can interact with it.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...