frontend
21 Apr 2016notes about frontend development in Rails.
- layouts
<body>
classes- Sass mixins
- media queries
- using images
- about mobile-first and responsive design
- CSS properties
- CSS recipes
- using Ajax requests
layouts
looking up current layout in Rails (more specific match wins):
- application layout (app/views/layouts/application.html.slim)
- controller-specific layout (app/views/layouts/photos.html.slim)
layout 'header'
declaration in controller (can be overriden by child controllers)render layout: 'header'
:- in controller action to render action view (overrides controller’s layout)
- in view itself with view content passed in block argument (renders view in specified layout keeping controller’s layout)
<body>
classes
consider adding 2 classes to <body>
of each page in application.html.slim
:
"c-#{controller_name}"
"p-#{controller_name}-#{action_name}"
it will make it easier to bind common CSS rules for all actions of controller:
.c-sites
padding: 10px
...
instead of:
.sites-index,
.sites-new,
.sites-show
padding: 10px
...
also it will make it natural to have this file naming scheme:
- app/assets/sites.sass for controller-wide CSS rules
- app/assets/sites/show.sass for page-specific CSS rules
Sass mixins
media queries
in CSS2 they were called media types, in CSS3 - media queries.
media queries are used to define different style rules for different media types after checking media features:
- width and height of the viewport (display area, browser window)
- width and height of the device (computer screen)
- orientation (landscape or portrait)
- resolution
for styles rules (CSS) to be applied media queries must resolve to true (media type matches device and all media features are true).
media query syntax (using SASS):
@media not|only media_type and (media_feature)
CSS
- specifying media type can be omitted (
all
media type is used by default) only
keyword is used to hide associated CSS from older user agents (they consider it to be unknown media type and just ignore associated CSS) - new user agents ignoreonly
keyword
media types (devices):
all
(default)screen
(screens, smartphones, tablets - most common)print
(printers)speech
(screenreaders)
other media types are deprecated.
media features:
max-height
/max-width
(max height or width of viewport)min-height
/min-width
(min height or width of viewport)max-device-height
/max-device-width
(max height or width of device)min-device-height
/min-device-width
(min height or width of device)
using images
Rails asset tag helpers:
javascript_include_tag
(commonly used in<head>
section)stylesheet_link_tag
(commonly used in<head>
section)image_tag
video_tag
audio_tag
asset tag helpers don’t check presence of assets at specified locations.
images are placed in app/assets/images/ and can be used in:
- view (
image_tag
helper) - CSS:
background: image-url('logo.png')
(Rails path helper - image is loaded in separate request)background: inline-image('logo.png')
(Compass inline data helper - embeds image directly in CSS)
about mobile-first and responsive design
mobile-first design
NOTE: the article contains many tips on how to design for mobile.
advantages of mobile-first design:
- easier to extend for larger screens than vice versa
- serving styles without media queries is faster (important for mobile devices)
- serving mobile styles first supports more devices (some devices might not support media queries at all)
ALWAYS CHECK mobile design on mobile phone itself (or at least using device mode of Developer Tools in Chrome)!
responsive vs. adaptive design
- responsive design:
@media (min-width: X)
- adaptive design:
@media (min-device-width: X)
my implementation of mobile-first responsive design
NOTE: container div is parent of both sidebar and main divs.
- mobile version (primary):
- stack sidebar and main divs on top of each other
(using default
display: block
)
- stack sidebar and main divs on top of each other
(using default
- desktop version (activated by media query):
- make sidebar div float to the right
(alternatively use
display: inline-block
for sidebar and main divs) - set container
max-width
property - fix all paddings and margins
- make sidebar div float to the right
(alternatively use
CSS properties
border
border: 0
(short forborder-width: 0
)border: none
(short forborder-style: none
)
cursor
- always use
cursor: pointer
for clickable elements - by default
cursor: auto
is used - browser sets a cursor
when using default cursor I couldn’t click search
icon at the bottom (1-2 px) -
probably the very tip of default cursor is not active (can’t be used for pointing).
position
position: absolute
:
right
sets offset to the left of right border of parent containermargin-right
sets a new offset relative to the offset set byright
width
vs. max-width
http://www.w3schools.com/css/css_max-width.asp
max-width
>width
=>width
max-width
<width
=>max-width
z-index
z-index
property is used to specify z-order (the stack order) of elements:
- by default
z-index: auto
(element has the same stack level as its parent) - element with higher z-index is in front of element with lower z-index
- works on positioned elements only (with position
absolute
,relative
orfixed
)
avoid using sequential numbers that don’t count from 1 (e.g. 5,6,7..
)
as it might lead to confusion about where missing z-indexes (1,2,3,4
) are.
prefer sequences 1,2,3...
(without gaps) or 100,200,300...
.
z-index
property has no effect on SVG elements: first elements are painted first.
CSS recipes
see CSS recipes.
using Ajax requests
views/foos/edit.html.slim:
- form_url = foo_url(@foo)
= simple_form_for @foo, url: form_url do |f|
= f.input :name, input_html: { data: { 'form-url': form_url } }
assets/javascripts/pages/foos/edit.coffee:
@on 'turbolinks:load', 'c-foos', ->
$foo_name_input = $('input#foo_name')
$foo_name_input.on 'blur', ->
$.ajax
url: $foo_name_input.data('form-url'),
type: 'PATCH',
dataType: 'json',
data:
foo:
name: $foo_name_input.val()
success: (data) ->
if data['valid?']
console.log "foo #{data.id} updated"
$('span.foo_name').html(data.name)
else
errors = JSON.stringify(data.errors)
console.log "foo #{data.id} not updated: #{errors}"
controllers/foos_controller.rb:
def update
@foo.update(update_params)
respond_to do |format|
format.html { redirect_to(foos_url) }
format.json
end
end
views/foos/update.json.jb:
@foo.attributes.merge(
valid?: @foo.valid?,
errors: @foo.errors.messages
)