9

I'm interested in building a highly modular Craft site in which my folder structure might look something like this:

- craft/
- modules/
  - hero-banner/
      index.html
      scripts.js
      styles.css
  - carousel/
      index.html
      scripts.js
      styles.css
- templates/
    404.html
    index.html
    _layout.html
index.php
robots.txt
.htaccess

An example of what hero-banner/index.html would look like:

<div class="hero-banner__contain">
  <div class="hero-banner__content">
    <h2 class="hero-banner__title">{{title}}</h2>
    <p class="hero-banner__subtitle">{{subtitle}}</p>
  </div>
  <img src="{{image}}" alt="{{title}}">
</div>

An example of how you'd use this in a Craft template like templates/index.html:

{{
  craft.componentUI.run('hero-banner/index', {
    title: 'Welcome to my site!',
    subtitle: 'This is an intro paragraph that has a bit more detail about this site.',
    image: 'http://dummyimage.com/800x350/000000/555555'
  })
}}

A couple reasons I'd like to do something like this:

  1. To create a parity between a style guide and the live site. This way a change to hero-banner/index.html would be reflected in the style guide and the live site. I understand a partial can do the same thing, but I'd prefer something like this because...
  2. We can encapsulate a module's HTML, CSS and JavaScript in one place. Doing this will allow us to utilize Git or npm to manage these modules individually if we'd like to use them on other sites. It also makes locating a module's assets much easier since they're centrally located rather than being in 3 different folders.

I've found a way to reference template files outside the standard templates/ directory to make this work. So I know this sort of setup can work.

However, my question is what problems could arise from this type of setup? Do I need to be concerned about performance/security/something else entirely?

Edit: Just for further clarification, I'm not looking to serve my CSS and JS through Craft. I want to keep my CSS and JS as-is, but wondered if I could call the modules from those directories (outside of the normal Craft templates).

9

So a few potential things to consider:

  • You do not want to be serving static assets like Javascript, CSS, etc. out of the templates directory. I realize you're planning to put them "elsewhere", but ensure that elsewhere is in the /public/ folder, so that php, Yii, and Craft do not have to be spun up just to serve a static asset. Do not use a Craft routine/plugin to load them.

  • The entire point of modularization is that the payoff is down the road as you're able to reuse components on future projects by just "plugging them in". I've seen this approach taken on many types of software projects, from desktop apps to mobile apps to websites. It almost never works out as well as people think it will, unless it is seriously well architected, because we're generally not a factory churning out widgets. There are things unique to each website beyond the basic functionality that requires customization; writing something that is both that general, and also useful is difficult. Think of how many hours you have spent fighting even well-implemented frameworks like Bootstrap or Foundation.

  • Most things of this nature either end up being way, way over engineered and bloated, with a ton of time spent on them, and the promised gains down the road in terms of time savings never being realized due to the aforementioned issues.

Just my two cents.

  • Just for clarification, I'm not looking to serve JavaScript or CSS out of the template directory. That might have been a tad confusing. I should've indicated in my example folder structure that the templates/ directory is still part of the equation here. I've updated my question with this clarification. But point taken with your other statements! – Aaron Bushnell Jun 1 '16 at 23:36
6

I've been making use of macros to create a component based architecture in a recent project. I've found this way of working useful in a few different ways:

  • Jumping around your codebase is much easier as you only have to remember what you've named your components or their parent folder to find and edit them.
  • It keeps your main templates easier to scan.
  • You can quickly see what components are being used in a template.
  • It helps to maintain consistency when components are re-used across the site.
  • I haven't noticed any hit regarding performance

I do try and make these components are simple as they can be to help them be as reusable as possible. An example would be not adding a grid container around the component and instead setting this in my template - this goes a small way to combating those specific use cases on a page level.

In terms of keeping your CSS and JS inside these component folders, I'd stay away from using Craft to fetch these for you like khalwat mentioned. I set up a similar structure for my SASS files _components/heroBanner/heroBanner.scss.

Taking your hero-banner as an example:

Folder structure:

- templates/
  _components/
    image/
      image.twig
    heroBanner/
      heroBanner.twig

heroBanner.twig

{% macro heroBanner( title, subtitle, image ) -%}
  {% from '_components/image/image' import image %}
  <div class="hero-banner__contain">
    <div class="hero-banner__content">
      <h2 class="hero-banner__title">{{ title }}</h2>
      <p class="hero-banner__subtitle">{{ subtitle }}</p>
    </div>
    {{ image( image, title ) }}
  </div>
{% endmacro %}

Used in a template

// Import components used for that template
{% from '_components/heroBanner/heroBanner' import heroBanner %}

<header>
 {{ heroBanner( 'Welcome to my site!', 'This is an intro paragraph that has a bit more detail about this site.', http://dummyimage.com/800x350/000000/555555' }}
</header>

I've also used the folder structure to show how components can be made up of other smaller related components:

_components/
  basket/   
    basketDropdown.twig
    basketFull.twig
    basketItemFull.twig
    basketItemMini.twig
    basketLogic.twig
  • Thanks for this info, Luke! I'll take a peek at this, but this is similar to what I was thinking about modularizing the HTML and Craft components. Very slick approach. – Aaron Bushnell Jun 8 '16 at 0:41
3

Some good answers already by Luke and khalwat.

I wouldn't try to load any templates outside of the normal Craft folder... you get into plugin territory there and it's not necessary. Your templates should always live in the templates folder.

That leaves what to do with your static resources.

Inline JS/CSS?

One thing I didn't see brought up in either answer was inlining the styles and JS. If you only have a few lines, why not inline them? Craft provides the {% includeCss %} and {% includeJs %} to handle cases just like this. That'll keep your modules nice and tidy.

Let the webserver handle it

As khalwat mentioned, Craft is happy to serve up those static resources for you but it'll fire up PHP to do so.

You can actually work around this with by intercepting the request before it gets to Craft.

For example, nginx lets you change the default root to a different directory, based on how you want to handle the request. So a request for \\example.com/resources/style.css would be served out of the resources directory inside your templates folder.

location ^~ /resources {

   alias /path/to/craft/templates/resources;

   # make sure twig/html templates aren't sent directly 
   location ~* \.(?:html|twig)$ {
     rewrite ^ /index.php?p=404;
   }

  location ~* \.(?:css|js)$ {
    try_files $uri $uri/ /index.php?$query_string;

  }
 }

This will break Craft's routing of course (#6) but only for files below /resources/.

Apache has alias which works similarly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.