Apr 27 2016
Apr 27

typey has just turned 1.0, so I thought it would be a great time to show off some of its features.

With typey I set out to write an easy to use typography framework that didn’t try to reinvent the wheel of web typography - a viable replacement for Compass's Vertical Rhythm to help the move from ruby to node-sass. I wanted a clean and simple way to layout type and combine various css properties for consistent use. So often in front end development we are disecting various design files to interpret font sizes and line heights so we can then replicate them inside our sass as variables. Often we'll add to that our own custom maths to convert the values into relative units, or use something like Vertical Rhythm instead. typey does all that for you, and it's beautifully simple.

Define some defaults

You will start defining your various sizes as pixels (which are a joy to work with), and then typey will go ahead and convert them to relative units (either rems or ems). At its simplest you can define a base-font-size, a base-line-height-ratio, and then create a SASS map of font sizes, and typey will do the rest for you.

In the example below we’ve defined our defaults and started laying out type. The font-size map uses t-shirt sizes as best practice (but you can use any keys you like), and it makes keeping track and retrieving a large number of sizes a breeze. Typey will use ratio based unitless line-height, and spit out rem px fallbacks as needed (you can toggle them on and off). If you want to specify your own ratio for line-height you can do so too.

$line-height-method: ratio;

$base-font-size: 16px;
$base-line-height-ratio: 1.5;

$font-size: (
  xl: 32px,
  l:  24px,
  m: 16px,
  s: 12px,
);

html {
  @include define-type-sizing;
}

h1 {
  @include font-size(xl);
}

h2 {
  @include font-size(l);
}

Working with typefaces

New in typey 1.0 is the ability to define and recall typefaces. This is particularly handy when you have a few common properties you want to use consistently with a particular font family.

$sans-serif: "Helvetica Neue", Helvetica, sans-serif;

$typefaces: (
  sans-serif: (
    font-family: $sans-serif,
    letter-spacing: -.5px,
    weight: bold,
    case: uppercase
  );
);

h1 {
  @include typeface(serif);
}

Currently the typefaces map lets us set common font-family, letter-spacing, weight (font-weight) and case (text-transform) for an entire typeface. Of course the only required property is font-family, everything else can be skipped if it's not needed. Letter spacing is always outputted as an em value (calculated from the base-font-size) so it will be relative to your font-size.

Advanced typesetting

Another new feature in typey 1.0 are typestyles, which let us set a font-size, line-height, weight and case for elements. Think of a typestyle as a really convenient way to pair these properties so they are always expressed consistently.

$typestyles: (
  h1: (
    font-size: xl,
    line-height: 1.25,
    weight: bold
  ),
  h2: (
    font-size: l,
    line-height: 1.35,
  ),
  h3: (
    font-size: m,
    line-height: 1.5,
    case: uppercase  
  )
);

h1 {
  @include typeset(h1);
}

You can define any keys you like, with font-size being the only required property. The typeset mixin lets you recall any typestyle easily. In the above example I’ve used keys from the font-size map to define font-sizes however you can just as easily input pixel values instead.

Ratio vs Rhythm

You might notice at the top of the very first example is a variable where you can set a line height method. There are two options here: ratio and rhythm. This is where typey completely takes over from Compass’s Vertical Rhythm and lets you define a common unit of measurement (the base-line-height) and then set line-height, margins and padding as a multiple of that unit. And it’s really easy to use.

$line-height-method: rhythm;

$base-font-size: 16px;
$base-line-height: 24px;

h1 {
  @include line-height(2);
  @include margin(0 0 1);
}

Keep on using px

There is no reason you need to use multiples of base-line-height for spacing as typey’s font-size, line-height, margin, and padding mixins can all take px values and then do the conversions to relative units.

h1 {
  @include margin(4px 0 8px 25px);
}

Of course it’s not really best practice but handy none the less.

Debugging

To make things a little easier you can spit out debug grid lines that will give you a pixel perfect overlay to get things lined up exactly as you need to.

$typey-debug: true;
$typey-debug-color: red;

Just use the typeset (or type-layout) mixin and the grid code gets added automatically. When you’re done just turn off typey-debug. If you want to add debug lines to any element that isn’t using type-layout or typeset, you can do it manually as so:

h1 {
  @include typey-debug-grid(1);
}

The argument takes either a ratio or rhythm value depending on how you are working.

Pro tip: Use shorthand

The typefaces and typestyles maps accept shorthand so these can be even quicker to express. typey’s shorthand is very forgiving and you need only remember to include the font-family and font-size as the first values in each map.

$typefaces: (
  sans-serif: $sans-serif -.5px,
  serif: $serif
);

$typestyles: (
  h1: xl 1.25 bold,
  h2: l 1.35,
  h3: m 1.5 uppercase
);

Where to now

Although the typey website (typey.io) is not up and running yet, there is comprehensive documentation included in the repo inside each scss partial. So to get an in depth look at each mixin, variable and function typey uses take a look inside the stylesheets directory.

It’s on npm, ruby-gems and bower and it works great with either ruby-sass or node-sass (and eyeglass). Try it out now, you won’t turn back.

Checkout typey on github. typey is also included in Zen 6, so now is a great time to take a look.

Sass Front End Development typography
Jun 29 2015
Jun 29

drupalgovcon logoWe’re excited for Drupal GovCon hosted in the DC area July 22nd through the 24th! We can’t wait to spend time with the Drupal4Gov community and meet fellow Drupalers from all over! Forum One will be presenting sessions in all four tracks: Site Building, Business and Strategy, Code & DevOps, and Front-end, User Experience and Design! Check out our sessions to learn more about Drupal 8 and other topics!

Here our are sessions at a glance…

Nervous about providing support for a new Drupal site? A comprehensive audit will prepare you to take on Drupal sites that weren’t built by you. Join this session and learn from Forum One’s John Brandenburg as he reviews the audit checklist the our team uses before we take over support work for any Drupal site.

Drupal 8’s getting close to launching – do you feel like you need a crash course in what this means? Join Forum One’s Chaz Chumley as he demystifies Drupal 8 for you and teaches you all that you need to know about the world of developers.

If you’re wondering how to prepare your organization for upgrading your sites to Drupal 8, join WETA’s Jess Snyder, along with Forum One’s Andrew Cohen and Chaz Chumley as they answer questions about the available talent, budgets, goals, and more in regards to Drupal 8.

The building blocks of Drupal have changed and now’s the unique time to rethink how to build themes in Drupal 8. Join Chaz Chumley as he dissects a theme and exposes the best practices that we should all be adopting for Drupal 8.

Drupal 8’s first class REST interface opens up a world of opportunities to build interactive applications. Come learn how to connect a Node application to Drupal to create dynamic updates from Forum One’s William Hurley as he demonstrates the capabilities of both JavaScript and Node.js using Drupal, AngularJS, and Sails.js!

Are you excited to launch your new website, but getting held down by all the steps it takes for your code to make it online? On top of that, each change requires the same long process all over again… what a nail biting experience! Join William Hurley as he demonstrates the power of Jenkins and Capistrano for managing continuous integration and deployment using your git repository.

If you’re a beginner who has found the Views module confusing, come check out this session and learn important features of this popular module from Leanne Duca and Forum One’s Onaje Johnston. They’ll also highlight some additional modules that extend the power of Views.

Have you ever felt that Panels, Panelizer and Panopoly were a bit overwhelming? Well, come to our session from Forum One’s Keenan Holloway. He will go over the best features of each one and how they are invaluable tools. Keenan will also give out a handy cheat sheet to remember it all, so make sure to stop by!

Data visualization is the go to right now! Maps, charts, interactive presentations – what tools do you use to build your visual data story? We feel that D3.js is the best tool, so come listen to Keenan Holloway explain why you should be using D3, how to use D3’s visualization techniques, and more.

Implementing modular design early on in any Drupal project will improve your team’s workflow and efficiency! Attend our session to learn from our very own Daniel Ferro on how to use styleguide/prototyping tools like Pattern Lab to increase collaboration between designers, themers, developers, and your organization on Drupal projects.

Are you hoping to mentor new contributors? Check out this session where Forum One’s Kalpana Goel and Cathy Theys from BlackMesh will talk about how to integrate mentoring into all the layers of an open source project and how to develop mentoring into a habit. They’ll be using the Drupal community as an example!

If you’re a beginner looking to set up an image gallery, attend this session! Leanne Duca and Onaje Johnston will guide you in how to set up a gallery in Drupal 8 and how to overcome any challenges you may encounter!

Attend this session and learn how to design and theme Drupal sites using Atomic Design and the Drupal 8 CSS architecture guidelines from our very own Dan Mouyard! He’ll go over our Gesso theme and our version of Pattern Lab and how they allow us to quickly design and prototype reusable design components, layouts, and pages.

Can’t make it to all of the sessions? Don’t worry, you’ll be able to catch us outside of our scheduled sessions! If you want to connect, stop by our table or check us out on Twitter (@ForumOne). We can’t wait to see you at DrupalGovCon!

Previous Post

Programmatically Restricting Access to Drupal Content

Next Post

Announcing Selectability.js: Style-able, Accessible Select Fields

May 07 2015
May 07

Beakers and science equipment. The beakers are filled with patterns instead of plain liquids.

Come check out our presentation at Drupalcon 2015 in Los Angeles about modular design on Thursday, May 14, 2015 at 1:00 – 2:00pm PST.

You’ll learn how to use styleguide/prototyping tools like Pattern Lab to increase collaboration between designers, themers, developers, and clients on Drupal projects. A focus on modular design early on in a Drupal project improves workflow and efficiency for the whole team!

After applying modular design principles into your design workflow you will have, guaranteed *:

  • Shinier, more polished sites: You’ll improve collaboration between themers and designers without relying so much on static photoshop comps, dramatically improving the end product’s quality at a higher detail level.
  • Happier clients: Clients will be able to see functional visual designs earlier in the project and be able to play with the site in an easy to use interface.
  • Happier developers: Developers can concentrate on the hard work of building the site while themers and designers concentrate on the visual design.
  • Project managers overcome with joy: Sites will be more easily themed, front-end bugs will be caught earlier, clients can see progress sooner, designers will be less bogged down in Photoshop iterations, and projects will be more successful.

We hope to see you there. It should be a lot of fun and we are genuinely interested in hearing your thoughts. If you are impatient and want to learn more about Pattern Lab and design patterns in general, take a look at this blog post by Brad Frost on designing pattern flexibility.

* not an actual guarantee. Results may vary. Consult your doctor if your clients remain happy for over four hours

Previous Post

Telling Simple (and Complex!) Stories with Open Data

Feb 07 2013
Feb 07

Lullabot Re-Launches a Responsive GRAMMY.com for Music’s Biggest Night

Lullabot is excited to announce its fourth annual redesign and relaunch of GRAMMY.com! Just in time for the 55th Grammy Awards this Sunday, February 10th, this year's site features a complete redesign, as well as an upgrade to Drupal 7 leveraging Panels and Views 3.x, some cool, fullscreen, swipeable photo galleries and other mobile-web bells and whistles. The site is also fully responsive, allowing the 40 million+ expected viewers to stream, share, and interact across any web or mobile device. Special congratulations to Acquia for hosting GRAMMY.com for the first time, as well as to our good friends over at Ooyala for once again delivering the site’s archive video content, and to AEG for delivering the live stream video via YouTube.

Simultaneously tune in to every aspect of music’s biggest night so you won’t miss a thing: awards news, video, pictures, artist info, Liveblog posts, thousands of tweets-per-minute and behind-the-scenes action.

The GRAMMY Live stream starts tomorrow, Friday February 8th at 5pm ET / 2pm PT, so you can spend all weekend with the website as you gear up for the CBS telecast on Sunday night. Don't miss any of the GRAMMY action this weekend!

We'll be watching and tweeting along from @lullabot. Follow us on Twitter and let us know what you think.

Feb 07 2013
Feb 07

Design Principles for a Multi-device World

Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of courage -- to move in the opposite direction.
— Albert Einstein

I would argue that a huge part of that genius Einstein refers to can be found in clarity of purpose and principles.

We all wind up in those situations where we're focusing on technical details, implementation and points of process, and missing the bigger picture. I confess I've been there far too often. When we find ourselves in those situations as designers, it's important to have some guiding principles we can remind ourselves of and even share with our team and colleagues. Guiding principles can help get everyone on the same page and make it easier to work through the details of process and implementation. They're no panacea, but they've certainly helped me maintain my sanity.

Below I've documented some of my emerging, fundamental design principles. These principles have helped guide me in this brave new world of a bazillion devices and amazing possibilities. Hopefully they'll be helpful to you as you hone your design process, document your own principles, and face challenges along the way.

    The mobile web is important!

    Secret: 98% of the following three paragraphs I learned directly from Luke Wroblewski. If you need help making the case for a focus on mobile, read his writing, see him speak, get in touch with him!

    Why care so much about mobile in our design process? By Q1 of 2012 Apple released numbers that showed there were now more iPhones sold every day than babies born in the entire world (300k babies to 402k iPhones)! That was just iPhones, there were actually 562k iOS devices (which includes iPod Touch and iPad) sold each day at that time. By Q1 2012 we'd also reached 700k Android devices activated per day, 200k Nokia smartphones and 143k Blackberry devices. According to Morgan Stanley Research, by 1990 there were 100M+ desktop internet users. By the early 2000's we had reached 1B+ desktop internet users. Today that number of desktop internet users is only slightly higher than it was in the early 2000's, yet the number of mobile internet users is now 10B+! The number of mobile devices on the planet surpassed the number of humans on the planet nearly two years before Morgan Stanley's research predicted it would, which means mobile is not only ubiquitous, it's growing faster than our expectations.

    But wait, there's more! In Q1 of 2012 Facebook announced they were seeing more people accessing Facebook on the mobile web than from ALL of their top mobile native apps combined! Facebook also released data suggesting that mobile web and app users invested noticeably more time on site than all of their Desktop web users combined. In Q3 of 2011 Nielsen US released their research on mobile users showing that of the millions and millions of mobile users across all platforms, significantly more were using the mobile web as opposed to native apps when given the choice (57M vs 49M).

    Create once, publish everywhere.

    Editorial teams need a singular, simple workflow to produce content once that then gets distributed efficiently and effectively to all device types. Editorial teams need to be focused on content quality, NOT things like device level placement, layout and aesthetic style. When developing your content model, model content types on core editorial and business needs, with an eye towards multi-channel reuse. You can then use those building blocks in the design process. This will ensure that editors aren't forced to become "designers by default". Ideas about form, structure and presentation that create new and more complex processes for editorial teams should be viewed with skepticism and caution. Anything that slows the editorial process, without adding significant content value, damages the core value in your product. A COPE approach (Create once, publish everywhere), with a consistent content model and simple data feeds that can be used by web-based widgets, apps, and business partners, helps facilitate rapid experimentation and innovation. It ensures that experimentation can happen at "the edges" without requiring foundational infrastructure changes.

    Editorial workflow is important!

    It's very easy for design teams to become focused on the consumption experience for content on a website, while completely ignoring how said content is created, reviewed, edited, curated and published. Great consumption experiences begin with great creation experiences. Spend time with the authors, reviewers, editors and publishers early in your design process. Watch what they do. Learn about the content they're producing. Gain an understanding of things like volume (how much of it do they produce), frequency (how often do they produce it) and average length (how much content makes up a single piece) for every type of content they're producing. As a designer, you can't create innovative ideas for new components and interaction methods without really understanding the content, and the best way to understand the content is to spend time with the people who create and nurture it.

    The second part of this principle is that bad or painful editorial workflows create content problems. Also, eliminating editorial workflow pain points makes happy clients. You may not be able to solve all the problems of an editorial workflow process as a designer, but you can play your part in the process by treating it as important.

    Release early and often.

    "Write drunk, edit sober."
    — Ernest Hemingway

    Always err on the side of the simplest viable product for each release (see KISS principle as well as Getting Real). Make quick decisions, make something, find out how users interact with it and what they're valuing. Discover pain points. Adapt. In a competitive market place we need to iterate quickly and fail gracefully. Failing is necessary for innovation, and we can't fail till we try something. Create a culture of rapid experimentation as opposed to analytical paralysis.

    Make existential design decisions based on data, 
not assumptions.

    By "existential design decisions" I mean decisions about whether a particular piece of content or component should exist on the screen. The basic rule here is don't remove things from a mobile experience because you assume mobile users don't want it. Conversely, don't add additional elements to a desktop experience because you assume those users want "enhanced experiences." Begin by delivering one content model and architecture across all devices, and then let real user data drive device specific optimization and customization.

    Mobile users will tell us what they're wanting as they use things (or don't use things). Their interaction patterns, values and preferences can guide optimization and customization, but not until we have them. We need to release something and watch people use it before we form assumptions (see earlier release early and often principle).

    Begin with the basic question of "Is this valuable for users?", not "Is this valuable to users on a particular device type or screen size?". While we may make some assumptions about hierarchical discrepancies from one device type to another, always start from the assumption that if it's important to users, it's important to ALL users.

    It's worth noting that gathering web-based metrics about the behavior of mobile users is easier than logging and tracking the detailed interactions of mobile app users. The mobile web experience can lead the way for us, providing the data we need to understand user values and interactions. Mobile users continue to defy expectations as to what they will do and want to do on their mobile devices. A common frustration for mobile web users happens when assumptions are made about what mobile users do NOT want or need from a desktop experience. It's extremely important that we not limit mobile users based on these assumptions. Creating tailored experiences with unique content models and components for different devices can create significant user experience problems. For example, lets imagine google indexes the desktop version of a website, and provides links to said content on mobile devices based on a search. If those mobile devices then redirect to a tailored site with a limited content model, editing out the content that was searched against, confusion and user frustration ensues. We must never dumb down or limit a desktop experience and call it a mobile experience!

    Design from content outward (not device type or display inward).

    Focus first on delivering the best and simplest possible experience of a complete content model across all devices. Design should begin by uncovering the most valuable type(s) of content, and designing an experience for those. All subsequent displays and views into that content should follow. For example, a news site could begin by determining the most valuable type(s) of news content they provide to their consumers. A design team can then begin researching, wireframing, prototyping and brain storming around the consumption experience of a representative piece of content from each of those types. Once that is fleshed out, the focus can shift to the various structural channels through which parts of that content type are displayed (e.g. a homepage, a top level category landing page, etc.).

    Nothing's more important than knowing what's important.

    "Design is the conscious effort to impose a meaningful order."
    — Victor Papanek

    Design is about helping people understand what's really important and meaningful. That's beautiful. Embrace it! Discover and understand the relative importance of each type of content, the pieces that make up that type of content, and the channels through which that content flows. You can't begin to apply visual hierarchy in design without first knowing the content hierarchy. Design decisions should begin with broad hierarchy evaluations. Develop a components list for each screen (a list of the discreet pieces or chunks of content that exist on the page) and assign a relative hierarchy (e.g. a 1, 2 or 3) to each component in the list. After all that, you can begin to work things out visually with placement, proportion, and style.

    Design mobile first.

    Once again, Luke Wroblewski has shined a spotlight on this and helped me understand it.

    Designing "mobile first" means that we embrace the constraints of a tiny screen early in our design process. We evaluate our content model, components list and hierarchy first with that tiny screen in mind. Once we've established that, we then ask if there are ways that hierarchy changes or interactions can be enhanced for users with screen sizes and bandwidth capabilities beyond mobile. The constraints of the mobile screen size help enhance focus during the design process and keep design teams more closely aligned with whatever the core product value is. It's like packing first in a carry-on suitcase to discover what you REALLY want to bring. Often, you'll find that those extra things you put in your larger suitcase never get worn or used.

    This does NOT mean that the visual experience can't be impressive. Remember, in many ways mobile devices have MORE capabilities than what's common among desktop devices. Things like device positioning, motion, location detection, multi-touch, gyroscope, and audio, video and photo input are common among mobile devices. Design teams may actually create more innovative and rich experiences focusing on mobile first during their design process.

    Optimize, then customize.

    After we actually make and release something, and have real user data to drive the next round of iteration and innovation, we need a way to prioritize that iteration. When both optimizations (e.g. technical solutions to serve up smaller file sizes or more appropriate ad sizes) and customizations (e.g. ideas about changes or enhancements to hierarchy, content or features) are being considered, optimizations should almost always be prioritized over customizations. Great experiences come from the ease and speed with which users can access, interact with, and contribute to content, and that ease and speed are very important. Mobile users continue to defy our assumptions about what they want to do on mobile devices, but they almost always want to do it faster and with greater ease.

    Create and maintain a visual language (NOT a myriad of distinct designs).

    Design teams need to produce a flexible visual language that can provide stylistic guidance across a myriad of screen sizes. There are some formal processes and design tools that can help you do this (e.g. element collages, style tiles, web style guides), but the core principle is to establish a visual language that can allow for quick design decisions across all breakpoints. This approach reinforces the "release early and often" principle above. Having a style guide and other tools to guide visual decisions, rather than a collection of concrete designs tied to specific device widths and scenarios, means that new experimental designs don't have to chart their own course. A design process that takes a tailored approach, providing a myriad of custom static comps can dramatically limit your ability to quickly respond and innovate.

    Jan 31 2013
    Jan 31

    Replicating module_invoke_all and drupal_alter in Javascript

    If you've ever written a Drupal module before you're likely familiar with Drupal's hook system. I'm not going to go in to details about how the hook system works, or why this particular pattern was chosen by Drupal's developers. What's important here is what this systems allows module developers to accomplish.

    At its most basic, the hook system is what allows me to write a module that enhances or extends Drupal -- without ever having to modify a line of someone else's code. I can, for example, modify the list of blocks that are available on a given page by simply implementing a "hook" function in PHP that modifies the information that was already set up. This approach is one of the things that makes Drupal incredibly flexible!

    When you're writing your own custom modules, it is customary to expose these types of hooks for other modules, too. That way, other developers can come along and make minor modifications or feature enhancements to your module by "piggybacking" on your module's functionality, rather than hacking your code. It also means that you don't have to anticipate every possible use case for your code: by providing these extension points, you allow future developers to extend it.

    Drupal makes it really easy for modules developers to do this, and it provides a set of helper functions that allow you to easily broadcast these "I have a hook! Who wants to tie into it?" announcements to the world.

    The case for APIs

    Now, that's all well and good, but what if the functionality I want people to be able to alter or events I want people to be able to react to are encapsulated in Javascript? This is where Drupal breaks down a bit and we're left to our own devices. Drupal provides a simple mechanism for modules to essentially register a bit of code that they would like to be executed whenever Drupal.attachBehavoirs is called. This happens when the DOM is fully loaded and Drupal's Javascript code has been properly initialized, and anytime new elements have been added to the DOM via AJAX. And that's about it.

    For most cases where Javascript needs to interact with Drupal this works just fine. What you're likely really after is some element in the DOM anyway so you can do your sweet web 2.0 fadeIn().

    Sometimes, though, your Javascript needs are more complex than adding visual pizzaz. Consider this; You've been asked to write a module that integrates a video player from a third party site into Drupal. The video service offers a straightforward Javascript based embed option. All you have to do is include their Javascript file on the page and call the player.setup() method, passing in an embed code to the player so that it knows which video to play. Easy enough, and a common pattern.

    Let's say the setup() method takes not only an embed code but also an array of additional paramaters to configure how the player appears and behaves. Some of those paramaters are callback functions -- the name of an additional Javascript function that should be called when certian things happen. Some examples of this might be 'onCreate' when the player is embeded and ready to start playback, 'onPause' when someone clicks the player's play/pause button, and so on. For our example we'll assume that we're implementing an 'onCreate' callback. It should be triggered by the video player after it's been embedded, and is ready for playback to start. (Another common example of something like this the jQuery.ajax, which can take 'success' and 'error' callbacks. Which one gets called depends on the result of the Ajax request.)

    This should be simple, right? Just set the callback to 'Drupal.myModule.onCreate' and write the corresponding function in your mymodule.js file!

    Except... Later on in the project, Kyle comes along and is told to implement an unrelated piece of functionality that also fade a DOM element in on the page after the video player has been embeded. Now two different functions both need to fire when the Video player has been created. You can't just pass in a second 'onCreate' callback function to the player.setup() method -- it only allows one value! So now Kyle is stuck trying to jam his unrelated Javascript in to your Drupal.myModule.onCreate function. Blam! You've got a mess of unrelated, hard to maintain code!

    A better way of handling this would be for your module to re-broadcast the 'onCreate' callback to give other code a chance to respond to it as well. You could take it one step farther and implement a system that sends out a notification when the 'onCallback' event occurs, and subscribe to it with any functions that need it. That approach would be a lot like the module_invoke_all() function in Drupal's PHP API.

    Lucky for you, there are all kinds of ways to do this in Javascript! I'll outline two of them below.

    The Drupal Way

    One way of solving the problem is to replicate the Drupal.behaviors system provided by core. That's actually pretty straightforward. You need to:

    • Create a well known place for someone to register their objects or functions.
    • Write a short snippet of Javascript that will loop through and execute these registered functions.
    • Call this Javascript at the appropriate time.
    • Ensure that your module's Javascript is loaded before that of other modules.

    In your javascript code, you'll need to create a standard object that other modules can go to when they register their functions. In core, this is Drupal.behaviors. We'll create our own new object for this example.

    var MyModule = MyModule || {};
    MyModule.callbacks = {};

    Then you'll need an easy way to call and execute any registered callbacks.

    MyModule.executeCallbacks = function(data) {
       $.each(MyModule.callbacks, function(key, callback) {
           if ($.isFunction(callback)) {
              callback(data);
           }
       });
    }

    What this code does is loop over all the functions collected in MyModule.callbacks and executes them. Pretty simple, really! It works well for notifying any code of some "event" as long as you remember to call the MyModule.executeCallbacks() method at the appropriate times.

    Now, any other module can register callback functions that will be called by the MyModule.executeCallbacks() method:

    MyModule.callbacks.theirModuleOnCreate = function() {
       // Do some sweet Javascript stuff here ...
    }

    Put it all together by implementing your onCreate callback (the code we wanted to implement at the very beginning of this exercise!) and call the new code.

    MyModule.onCreate = function() {
       // Give all modules that have registered a callback a chance to respond.
       MyModule.executeCallbacks();
    }

    Pretty painless. Just make sure your module's Javascript file is loaded before any others: in Drupal, you can do that by changing the weight of your module to -10, or something similar. If you don't do that, you'll end up with warnings about "MyModule.callbacks being undefined" when someone else's Javascript is loaded first, and tries to register a callback with your object.

    This approach is easy to implement, but it still has some problems.

    • It's a major "Drupalism." For anyone familiar with Javascript but not with Drupal's way of doing things, it's a conceptual hurdle that needs to be overcome before understanding how to add a new behavior.
    • If one behavior fails, the execution stops: anything that hasn't be executed will not get called, and you're dependent on others to write code that doesn't fail.
    • There is no easy way to remove a behavior added by someone else's code, or to overwrite the way that Drupal core does something. Don't like the table drag javascript? The only way around it is Monkey Patching.

    An alternative way

    Another approach that's a bit more "Javascripty" is to use the jQuery.trigger() and jQuery.bind() methods. With them, you can create custom events that other modules can listen for and react too. It's a lot like using jQuery to intercept the 'click' event on a link, perform some custom action, then allowing the link to continue with it's processing. In this case, though, we'll be triggering our own custom event on a DOM element. To do this you need to:

    • Call jQuery.trigger on an object or DOM element in order to broadcast an event.
    • Use jQuery.bind on an object or DOM element to register a listener for an event.
    • Wash, rinse & repeat ...

    As usual, the code samples below would go inside of your module's mymodule.js file and be included on the page when necessary via the drupal_add_js() PHP function.

    Inside of our module's .onCreate callback, we use the jQuery.trigger() method to trigger our custom event and alert all listeners that they should go ahead and do their thing. It's not necessary to prefix our event names with 'myModule.' but it does lead to cleaner code. (It also makes it easier to unbind all of the events associated with a particular module in one step.) This approach is functionally equivalent to calling the MyModule.executeCallbacks() method from the previous example. We're telling anyone that wants to participate that now is the time to do it!

    MyModule.onCreate = function() {
       // Trigger an event on the document object.
       $(document).trigger('myModule.onCreate');
    }

    The second piece of this puzzle is using the jQuery.bind() method to add an event listener that will be triggered any time our custom event is triggered. Each event listener receives the jQuery.Event object as the first argument. The code below is equivalent to the bit above where we register our callback with MyModule.callbacks.theirModule = {}

    $(document).bind('myModule.onCreate', function(event) {
       // Do my fancy sliding effect here ...
    });

    Any number of modules can bind to the custom event, and respond to the onCreate callback event, without ever having to modify your module's Javascript.

    Another technique that I've used in the past is to create a drupal_alter() style functionality in Javascript. This would allow others to modify the parameters that my code passes to a third party's API. It's easy to do, so since you can pass an array of additional arguments to the jQuery.trigger() method. They'll be passed along to any listeners added with jQuery.bind(). And, since complex data types in Javascript are inherently passed by reference, the listener can make changes to the incoming parameters and they'll be reflected upstream. Something like the following would do the trick.

    MyModule.createWidget = function() {
      var parameters = {width: 250, height: 100, onCallback: 'MyModule.onCreate'};
      // Allow other modules to alter the parameters.
      $(document).trigger('myModule.alterParameters', [parameters]);
      superAwesomeWidgetAPI().setup(parameters);
    }

    Then anyone else could bind to the new 'myModule.alterParameters' event and receive the parameters object as an additional argument. The first argument for any function using jQuery.bind() to listen to an event is always the jQuery.event object.

    $(document).bind('myModule.alterParameters', function(e, parameters) {
      // Here I can change parameters and it will be reflected in the function that triggered this event.
      parameters.width = 350;
    });

    While this method isn't perfect either, I like that it's closer to the Javascript programming patterns used in the broader world outside of Drupal. This means it's easier for someone not familiar with Drupal to understand my code and to quickly figure out how to work with it.

    It does, however, still exhibit some of the same problems as the Drupal.behaviors method. Notably the fact that if any one listener has code that fails the whole system breaks down. In addition, you have to trigger and bind to events on either a DOM element or other Javascript object.

    Summary.

    Drupal itself doesn't come with a Javascript equivalent to the module_invoke_all() function, but there are a lot of ways that we can implement a similar system ourselves. When you run in to this problem in your development, I encourage you to use the second approach outlined: it has all the same capabilities of the Drupal.behaviors approach, with less code and a shallower learning curve.

    These are by no means the only methods for accomplishing this sort of task in Javascript. Another for example would be the popular publish/suscribe pattern, but we'll wait to explore those in another article! Whichever approach you choose, it's important to build for future flexibility, just as you would with your PHP code.

    Dec 13 2012
    Dec 13

    If you've searched for anything online, you're probably familiar with the handy "number of results" counter that's often displayed alongside the matching documents. You know -- that nice "Displaying results 11–20 of 196"? Somehow, Drupal 7's core Search module still doesn't doesn't include that information on its standard results page!

    A lot of sites handle search with Apache Solr or use Search API to display search results in a View, and both make it easier to show a result count. For simple sites without many nodes, the core Search works just fine… except for the glaring omission of a result count. I wanted a quick solution, and I found one that worked for me.

    The best method I tried is to sneak a peak at the pager information. For any pager displayed—be it for search results or a View of blog posts or nodes promoted to the front page with no Views at all—the total number of items is stored in a global variable. This way, Drupal can determine the total number of pages in the set, so that it knows where to take you when you click the "Last" page link. This is an imperfect solution, but it was the best I've found. I started with Eric London's 2009 post for Drupal 6, and made some improvements from there. For example, the format of displayed result count depends on the total number of results:

    • 1: "Displaying 1 result"
    • 2 to 10 (one page): "Displaying 5 results"
    • 11 and above (multiple pages): "Displaying 11 - 20 of 44 results"

    There are three steps to add this result count to the page:

    1. Add a preprocess hook. I won't get into the details of preprocess hooks here (you can learn more about that from Drupalize.me). Open template.php in your theme directoy, and paste in the preprocess code provided below. Replace "THEMENAME" with the name of your theme.

    2. Copy search-results.tpl.php into your theme directory. You'll find the original in modules/search. Leave that intact and make a copy.

    3. Print the result code in the template. Edit your copy of search-results.tpl.php to print the search result count wherever you want. I put mine right below the "Search results" header tag.

    <?php print $search_totals; ?>

    Preprocess code

    /**
    * Implements hook_preprocess_search_results().
    */
    function THEMENAME_preprocess_search_results(&$vars) {
      // search.module shows 10 items per page (this isn't customizable)
      $itemsPerPage = 10;

      // Determine which page is being viewed
      // If $_REQUEST['page'] is not set, we are on page 1
      $currentPage = (isset($_REQUEST['page']) ? $_REQUEST['page'] : 0) + 1;

      // Get the total number of results from the global pager
      $total = $GLOBALS['pager_total_items'][0];

      // Determine which results are being shown ("Showing results x through y")
      $start = (10 * $currentPage) - 9;
      // If on the last page, only go up to $total, not the total that COULD be
      // shown on the page. This prevents things like "Displaying 11-20 of 17".
      $end = (($itemsPerPage * $currentPage) >= $total) ? $total : ($itemsPerPage * $currentPage);

      // If there is more than one page of results:
      if ($total > $itemsPerPage) {
        $vars['search_totals'] = t('Displaying !start - !end of !total results', array(
          '!start' => $start,
          '!end' => $end,
          '!total' => $total,
        ));
      }
      else {
        // Only one page of results, so make it simpler
        $vars['search_totals'] = t('Displaying !total !results_label', array(
          '!total' => $total,
          // Be smart about labels: show "result" for one, "results" for multiple
          '!results_label' => format_plural($total, 'result', 'results'),
        ));
      }
    }

    If you'd like a core solution for the search result count, consider rolling a patch on this old issue. The last attempt was nearly four years ago, so you may need to start from scratch. Feature freeze for Drupal 8 was just pushed back to Feb 18, so you've got time to get it done!

    Nov 02 2012
    Nov 02

    Listen online: 

    In this episode Addi talks with Emma Jane Hogbin, and Lullabots Sean Lange and Angus Mak about Drupal base themes. What is a base theme, and exactly what is that all about? Listen as we chat about these questions and more through our own experiences working with, and choosing base themes, along with some recommendations for themes to use in various situations.

    Podcast notes

    Release Date: November 2, 2012 - 9:00am

    Album:

    Length: 52:02 minutes (36.21 MB)

    Format: mono 44kHz 97Kbps (vbr)

    Oct 24 2012
    Oct 24

    Learn about this powerful responsive base theme

    We're happy to kick off our new series, Introduction to Omega 3.x. The Omega theme is the second most installed Drupal theme, and is used by over 40,000 sites. It is a very popular responsive base theme, with a lot of configuration options to help you get up and running. In the series we will be covering all of the pieces of your first Omega sub-theme, from an explanation of how a base theme works, all the way to making your own customizations to theme templates and functions from the Omega base.

    The first two videos in the series are free, so you can get a sense of what will be covered and get an overview of Omega, helper modules often used with it, the terminology it uses, and finding documentation.

    Omega Overview and Terminology

    A Brief Explanation of the Omega 3.x series

    Sep 27 2012
    Sep 27

    Lullabot's Drupal training site turns 2

    It's been almost 2 years since we launched Drupalize.Me and I'd like to take a moment to appreciate some of the site's recent accomplishments.

    Over 600 Videos

    A few weeks ago, Drupalize.Me product manager Addison Berry announced the 600th video posted to Drupalize.Me! Members now get access to over 233 hours of content on an immense range of Drupal-oriented topics from simple content creation to site building tricks and database server performance optimization. Drupalize.Me's most popular videos cover coding for and using Views, using the Calendar and Date modules, configuring WYSIWYG editors, Display Suite, Organic Groups, Drupal 7 module development, and more. The Drupalize.Me team has been really amazing – posting new videos every week and paying close attention to member requests for new topics.

    Over 2,000 Subscribers

    Word about Drupalize.Me has spread and I often see people on Twitter telling one another that for them, Drupalize.Me has become the way to learn and keep up with Drupal techniques. Drupalize.Me has iPhone/iPad, Android, and Roku apps so members can watch the videos on their mobile devices or televisions. Drupalize.Me has also partnered with Acquia to offer discounted memberships to Acquia Network subscribers.

    As word has been getting around about Drupalize.Me, subscriber numbers have been growing and recently crossed 2,000 simultaneous subscribers. We're reaching more people on a monthly basis than most large-scale Drupal events. We couldn't be more excited about the response!

    Drupalize.Me now has a staff of 3 full-time people creating videos, maintaining the site, adding features and handling customer support. This team is augmented by others at Lullabot who step in to help with expert video training, development, design and support. Drupalize.Me now represents more than 15% of Lullabot's budget and has become a great outlet for the Lullabot team to share the knowledge that we've gained building the high-profile websites that represent the majority of our work.

    New Features

    The Drupalize.Me team has been listening closely to subscriber feature requests and we've gotten lots of new features over the past 2 years. They've arranged videos into "series" collections and allow users to watch them consecutively. They've also added curated "guides" collecting videos into linear curriculum for different types of members. They've also greatly improved the user dashboard pages allowing users to manage their queue as well as see the listing of recently watched videos and even displaying a line graph so members can see their progress within the videos they've watched. The team also added the ability to store pause points and allow users to resume from exactly where they left off - even if they're resuming on a different device such as their phone or connected television.

    And speaking of connected televisions, we've got apps! We've got an iOS app for your iPhone/iTouch/iPad which can AirPlay to your AppleTV. We've also got an Android app and an app for the Roku Streaming Player box. You can pick up a Roku box for as little as $50, hook it up to your television, and watch all of the Drupalize.Me videos from your couch. It's a great way to learn.

    Group Memberships

    Drupalize.Me also offers group memberships. If you want Drupal training for your entire group, department, company, or institution, we offer that too. Group accounts greatly reduce the price of individual accounts while still allowing each group member to manage their own video queue, resume videos, and see their own history. We offer both managed group plans and IP-range plans to allow access to all devices at a physical location such as a library or campus.

    Subtitles, Transcripts & Translations

    Perhaps the greatest new features at Drupalize.Me are the ability to offer subtitles, transcripts, and translations for the videos. We have many international subscribers who, while they speak and understand English, sometimes can't keep up with the rapid-fire technical information in the videos. They've been asking for English-language subtitles and transcripts so they can follow along better. We're proud to say that we've added this functionality as well as functionality to provide complete translations with subtitles in other languages! 60 of our recent videos as well as the complete Introduction to Drupal guide already have transcripts and subtitles. And all new videos published on Drupalize.Me in the future will have transcripts and subtitles.

    We're currently looking for volunteers to do foreign language translation for Drupalize.Me. If you're a bi-lingual Drupalist and you'd like to help make bring these Drupal training videos to the world, please contact us!

    Drupalize.Me & Videola

    One of the Drupalize.Me team's biggest accomplishments is building the Drupalize.Me site itself. The team has built a great Drupal-based platform which manages both the permissions and delivery of adaptive bitrate streaming video; recurring subscription billing and administration; video content categorization, listing, and organization; mobile app and IPTV delivery with seamless pause-on-one-device-resume-on-another functionality; and now even multi-language subtitles and transcripts.

    As we've been building Drupalize.Me, we've been funneling this work and knowledge into Videola, a platform to provide this functionality to others wanting to build subscription-based or IPTV-oriented video sites. In short, Videola is a framework for building sites like Drupalize.Me... or like Netflix, Hulu, or Amazon video-on-demand. Videola can do everything that Drupalize.Me can do and more. If you'd like to build a site like this, please contact us and we can talk to you about getting you set up with a Videola site of your own.

    Onward

    Addi and the rest of the Drupalize.Me team have been doing a lot of training at DrupalCons, DrupalCamps, and other events. They've been very involved in the Drupal Ladder project and have posted a series of free videos to help new Drupalers get involved with core development.

    Drupalize.Me recently started its own podcast (which picks up where the long-running Lullabot Drupal Podcast left off). Every other Friday, the Drupalize.Me team is posting a new podcast with interviews and discussions to help listeners keep up with the ever-changing world of Drupal. The team is also constantly upgrading and improving the site and they've got lots of great feature ideas for the future.

    I couldn't be more proud of the work that's been done by Addi Berry, Joe Shindelar, Kyle Hofmeyer, and everyone who's helped with Drupalize.Me over the past 2 years. The site just keeps getting better and better. At this rate, I fully expect that 2 years from now I'll be bragging about their Drupal training neural implants and interstellar 3D streaming. But for now, I'm really happy with where we are – doing great, having fun, and sharing knowledge with people, empowering them to do great things.

    Sep 21 2012
    Sep 21

    Listen online: 

    Join Addi chats while she chats with Jen Lampton about her Drupal history, her current work in Drupal 8, and big, scary animals. Jen is leading up the charge to get a new theme system into Drupal core, based on the Twig template engine, so we talk quite a bit about what that means, how it would change Drupal, and how everyone can help make it happen.

    Podcast notes

    Release Date: September 21, 2012 - 10:00am

    Album:

    Length: 31:59 minutes (18.3 MB)

    Format: mono 44kHz 80Kbps (cbr)

    About Drupal Sun

    Drupal Sun is an Evolving Web project. It allows you to:

    • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
    • Facet based on tags, author, or feed
    • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
    • View the entire article text inline, or in the context of the site where it was created

    See the blog post at Evolving Web

    Evolving Web