Flexi is a lightweight, mobile first, performance focused layout framework for Ember. Flexi makes building layouts that are fluid, responsive, and platform aware fun, easy and fast.
Flexi does most of it's heavy lifting at build
time, happily adding very little
runtime code and CSS to your project.
- Documentation: https://html-next.github.io/flexi/#/docs
- Blog Post: A Tale of Two States: Modern Responsive Design illustrated with Ember & Flexi
- Talk: Responsive and Cross Platform Design
ember install flexi-layouts
That's all that required if you're using the classic component layout. If you are using a pod-based structure you will also need to install the shim for ember-app
. This is done by modifiying your ember-cli-build.js
file. This shim makes ember-cli's template tree able to find
the templates for layouts.
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var shim = require('flexi-layouts/lib/pod-templates-shim');
shim(EmberApp);
Join the flexi channel on Slack.
With flexi, you can separate your markup into layouts, one for each breakpoint name
defined in config/flexi.js
.
Given the following breakpoint names: mobile
, tablet
, desktop
.
Example:
ember g layout index/<size>
This will add the index/-layouts/<size>.hbs
file to your application.
Layouts are compiled into a single template.hbs
for the route or component
which will activate the correct layout based on booleans provided by the
device/layout
service.
Layout booleans are available for use in any layout template.
For instance, for the mobile
breakpoint, you can include a conditional block
with so:
{{#if FlexiLayout.isMobile}}
{{/if}}
This is useful if you only need one or two layouts, but still need to make some smaller modifications for other breakpoints.
Example File Structure:
This is an example route structure using routes
as the podModulePrefix
and layouts
app/
routes/
foo/
-layouts/
mobile.hbs
desktop.hbs
components/
foo-bar/
component.js
template.hbs
Layout attributes are converted to classes at build time, giving you the convenience of a nice attribute syntax and the performance of class based selectors.
<container>
and <grid responsive>
components are used as @container
breakpoints utilize a raf
polling technique to simulate element
specific resize events with high granularity.
Based on their width these components add .container-<breakpoint-prefix>
classes. This results in classes which utilize these breakpoints using
the width of the container instead of the width of the viewport.
Without rows
With rows
Container is an Ember component which sets it's class depending on it's current width and your defined
breakpoint prefixes. There are two forms of containers, <container>
and <grid responsive>
.
The container class will be of the form:
You can make any component a container by extending it with the flexi container mixin.
import Ember from 'ember';
import ContainerMixin from 'flexi/mixins/container';
export default Ember.Component.extend(ContainerMixin, {});
Layout elements give you a declarative syntax for quickly composing common layout situations.
Layout components allow you to use container based breakpoints instead of @media queries.
With flexi, you can build grids with or without rows. Rows are convenient for item height resets with flexbox. Columns respond to @media breakpoints, but they can also respond to the container they are in.
You can choose which css, columns, column classes, gutters, and breakpoints to include. It's fully configurable
Without rows
With rows
Without Columns
Flexi adds a layout service to your app.
app/services/device/layout
This service contains your breakpoints, as well as booleans which indicate when a given breakpoint is active.
This service also contains two booleans that can be used for orientation
needs. orientationIsLandscape
and orientationIsPortrait
.
Here's an example of the common "email client" layout implemented with flexi, it shows how this pattern makes it easy to build layouts that are responsive not only within a single route, but across routes.
router.js
this.route('emails', function() {
this.route('index', { path: '/' });
this.route('single', { path: '/:id' });
})
emails/route.js
export default Route.extend({
model() {
return RSVP.hash({
emails: this.get('store').findAll('email')
});
}
});
emails/index/route.js
export default Route.extend({
model() {
return this.modelFor('emails');
}
});
emails/components/email-list/template.hbs
emails/components/email-list/component.js
export default Component.extend({
tagName: ''
});
emails/index/-layouts/phone.hbs
emails/index/-layouts/tablet.hbs
emails/-layouts/phone.hbs
emails/-layouts/tablet.hbs
Settings
{
// the number of columns for the grid
columns: 12,
// optional, used for column classes: `${colPrefix}-${breakpointPrefix}-${columnNumber}`
columnPrefix: 'col',
// if false, @media css is not included
includeMediaCSS: true,
// if false, default element styles are not included
includeElementCSS: true,
// if true, will convert layout attributes on non-layout elements to classes as well
transformAllElementLayoutAttributes: false,
// grid and layout element gutters
gutterPadding: '.5rem',
// if false, no styles are included (trumps `includeMediaCSS` and `includeElementCSS`)
includeCSS: true,
// an array of breakpoints to use in your app (see below)
breakpoints: []
}
config.breakpoints
Your config must have a breakpoints
array. A breakpoint has the structure:
{ name: 'mobile', prefix: 'xs', begin: 0 }
name
will be used for blueprint generation of layout names, and is made available as an is<Name>
boolean on the device/layout
service.
prefix
is a shorthand for the breakpoint name used for column attributes, classes, and responsive utilities.
With a prefix
of xs
.
begin
is the pixel value at which the breakpoint becomes valid if equal to or larger than.
Using a breakpoint's prefix
.col-xs-1
... .col-xs-n
will be valid class names (if columnPrefix
is set to col
).
<box xs="n visible vertical">
Is valid shorthand for
<box class="col-xs-n visible-xs vertical-xs">
The following responsive utilities are made available for each prefix:
.hidden-xs,
.visible-xs,
.container-xs,
.vertical-xs,
.horizontal-xs,
.wrap-xs,
.nowrap-xs {}
- Open an Issue for discussion first if you're unsure a feature/fix is wanted.
- Branch off of
develop
(default branch) - Use descriptive branch names (e.g.
<type>/<short-description>
) - Use Angular Style Commits
- PR against
develop
(default branch).
Angular Style commit messages have the full form:
<type>(<scope>): <title>
<body>
<footer>
But the abbreviated form (below) is acceptable and often preferred.
<type>(<scope>): <title>
Examples:
- chore(deps): bump deps in package.json and bower.json
- docs(component): document the
fast-action
component
A special thanks goes out to @ebryn for the inspiration to pursue a solution for explicit layouts, and IsleofCode for providing the time to built it.