Frontal avanzado y assetic

Post on 20-Jan-2015

1.548 views 4 download

description

Presentacion sobre frontales multidevice, responsive design y preprocesadores css realizada en el deSymfony del 2013

Transcript of Frontal avanzado y assetic

¿Qué es Ofertix?

¿Qué es Ofertix?

•Vendemos productos y servicios por internet

¿Qué es Ofertix?

•Vendemos productos y servicios por internet•A precios interesantes

¿Qué es Ofertix?

•Vendemos productos y servicios por internet•A precios interesantes•Tenemos varias lineas de negocio

¿Qué es Ofertix?

•Vendemos productos y servicios por internet•A precios interesantes•Tenemos varias lineas de negocio•Ventas Privadas, Ocio, Full Price y Tienda Física

¿Qué es Ofertix?

•Vendemos productos y servicios por internet•A precios interesantes•Tenemos varias lineas de negocio•Ventas Privadas, Ocio, Full Price y Tienda Física•7 años en marcha

¿Qué es Ofertix?

•Vendemos productos y servicios por internet•A precios interesantes•Tenemos varias lineas de negocio•Ventas Privadas, Ocio, Full Price y Tienda Física•7 años en marcha•Principalmente Symfony 1.2

¿Que Problemas Tenemos?

¿Que Problemas Tenemos?

NINGUNO. SOMOS LOS MEJORES

Pero...

Pero...

•Nuestra interfaz móvil es mejorable

Pero...

•Nuestra interfaz móvil es mejorable•Queremos añadir full-text search

Pero...

•Nuestra interfaz móvil es mejorable•Queremos añadir full-text search•Queremos mejorar lineas de negocio

Pero...

•Nuestra interfaz móvil es mejorable•Queremos añadir full-text search•Queremos mejorar lineas de negocio•Ya toca un lavado de cara

Pero...

•Nuestra interfaz móvil es mejorable•Queremos añadir full-text search•Queremos mejorar lineas de negocio•Ya toca un lavado de cara•Y de cuerpo

Solución

Re-escribir el frontal de todos nuestros sites.

http://symfony.com/logo

Solución

Re-escribir el frontal de todos nuestros sites.

Aplicando lo aprendido de nuestros errores

http://symfony.com/logo

La remodelación del site tiene muchos temas interesantes

•Cache•Reverse Proxy•No-SQL•Full-text Search•UX•Gestión de sesiones•Recolección de datos•Big Data

•Socialización•Web Services•Javascript Avanzado (MVC)•Gestión de Statics•Colas•Optimización de Carga•Balanceo de pagos•...

Pero hoy toca...

Pero hoy toca...

Mobile y otros dispositivos

¿Por qué?

Fuente Google Analytics 04/06/2013

¿Por qué?Otros

6%Android Browser

11%

Safari IOS 16%

Safari Mac4%Firefox Windows

13%

IE Explorer23%

Chrome Windows27%

Fuente Google Analytics 04/06/2013

Mobile Design

Mobile DesignDevice Centric Design

•Una plantilla por tipo de device a cubrir.•Una hoja de estilos por device*•Un “Javascript” por device*•Detectamos el tipo de device al servir el site•Permitimos cambiar el tipo de plantilla servida.

Mobile DesignDevice Centric Design

Como lo hacemos

<?phpnamespace Solilokiam\RequestListenerBundle\EventListener;

use Symfony\Component\HttpKernel\HttpKernelInterface;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;

use Solilokiam\RequestListenerBundle\Detector\DeviceDetector;

class RequestListener{ public function onKernelRequest(GetResponseEvent $eve

nt)

{ $detector = new DeviceDetector();

$request = $event->getRequest();

$user_agent = $request->headers->get('user-agent');

if($detector->isMobile($user_agent))

{ $request->setRequestFormat('mobile', 'text/ht

ml');

} elseif($detector->isGameConsole($user_agent)) {

$request->setRequestFormat('game', 'text/html');

} elseif($detector->isTv($user_agent)) {

$request->setRequestFormat('tv', 'text/html');

} else { $request->setRequestFormat('html', 'text/html

');

} }}

<?phpnamespace Solilokiam\RequestListenerBundle\Detector;

class DeviceDetector{ //Incomplete regexs just educational purpouse;

private $mobile_device_regex = '/(alcatel|amoi|android|avantgo|blackberry|benq|cell|cricket|docomo|elaine|htc|iemobile|iphone|ipad|ipaq|ipod|j2me|java|midp|mini|mmp|mobi|motorola|nec-|nokia|palm|panasonic|philips|phone|playbook|sagem|sharp|sie-|silk|smartphone|sony|symbian|t-mobile|telus|up\.browser|up\.link|vodafone|wap|webos|wireless|xda|xoom|zte)/i'; private $game_console_device_regex = '/xbox/i'; private $tv_device_regex = '/(bravia|googletv)/i';

public function __construct() {

}

public function isMobile($user_agent) { if(preg_match($this->mobile_device_regex,$user_agent)) { return true; } else { return false; } }

public function isGameConsole($user_agent) { if(preg_match($this->game_console_device_regex,$user_agent)) { return true; } else { return false; } }

public function isTv($user_agent) { if(preg_match($this->tv_device_regex,$user_agent)) { return true; } else { return false; } }}

Como lo hacemos

parameters: solilokiam_request_listener.example.class: Solilokiam\RequestListenerBundle\EventListener\RequestListenerservices: solilokiam_request_listener.example: class: %solilokiam_request_listener.example.class% tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

<?php

namespace Solilokiam\FrontBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller

{ public function indexAction($name)

{ $format = $this->getRequest()->getRequestFormat()

;

return $this->render('SolilokiamFrontBundle:Default:index.'.

$format.'.twig', array('name' => $name));

}}

Como lo hacemos

<?phpnamespace Solilokiam\RequestListenerBundle\EventListener;

use Symfony\Component\HttpKernel\HttpKernelInterface;use Symfony\Component\HttpKernel\Event\GetResponseEvent;use Symfony\Component\HttpKernel\HttpKernel;use Solilokiam\RequestListenerBundle\Manager\DeviceRedirectManager;use Solilokiam\RequestListenerBundle\Manager\DeviceManager;

class RequestListener{ protected $device_manager; protected $redirect_manager;

public function __construct(DeviceManager $device_manager,DeviceRedirectManager $redirect_manager) { $this->device_manager = $device_manager; $this->redirect_manager = $redirect_manager; }

public function onKernelRequest(GetResponseEvent $event) { if(HttpKernel::MASTER_REQUEST === $event->getRequestType()) { $request = $event->getRequest(); $user_agent = $request->headers->get('user-agent');

$this->device_manager->detectDevice($user_agent);

$response = $this->redirect_manager->redirectIfNeeded($request);

if($response) $event->setResponse($response);

return; } }}

Como lo hacemos (pero mejor)

Como lo hacemos (pero mejor)

<?phpnamespace Solilokiam\RequestListenerBundle\Manager;

use Solilokiam\RequestListenerBundle\Detector\DeviceDetector;

class DeviceManager{ const DEVICE_MOBILE = 'mobile'; const DEVICE_GAME = 'game'; const DEVICE_TV = 'tv';

protected $session;

public function __construct($session) { $this->session = $session; }

public function hasDevice() { return $this->session->has('device'); }

public function setDevice($device) { $this->session->set('device',$device); }

public function getDevice() { return $this->session->get('device'); }

public function detectDevice($user_agent)

{ $detector = new DeviceDetector();

if($detector->isMobile($user_agent))

{ $this->setDevice(self::DEVICE_MOBILE);

} elseif($detector->isGameConsole($user_agent)) {

$this->setDevice(self::DEVICE_GAME);

} elseif($detector->isTv($user_agent)) {

$this->setDevice(self::DEVICE_TV);

} }}

<?phpnamespace Solilokiam\RequestListenerBundle\Manager;

//use \Solilokiam\RequestListenerBundle\Manager\DeviceManager;use Symfony\Component\HttpFoundation\RedirectResponse;

class DeviceRedirectManager{ const VIEW_MOBILE = 'mobile.desymfony.local'; const VIEW_GAME = 'game.desymfony.local'; const VIEW_TV = 'tv.desymfony.local';

protected $view_type; protected $device_manager;

public function __construct(DeviceManager $manager) { $this->device_manager = $manager;

if(!$this->device_manager->hasDevice()) { $this->view_type = ''; } else { $device = $this->device_manager->getDevice();

$this->view_type = $device; } }

public function redirectIfNeeded($request) { $host = $request->getHost();

$responseUrl = null;

Como lo hacemos (pero mejor)

$this->view_type = $device; } }

public function redirectIfNeeded($request) { $host = $request->getHost();

$responseUrl = null;

if($host != self::VIEW_MOBILE && $this->view_type == 'mobile') { $responseUrl = $this->generateRedirectUrl($request,self::VIEW_MOBILE); }

if($host != self::VIEW_GAME && $this->view_type == 'game') { $responseUrl = $this->generateRedirectUrl($request,self::VIEW_GAME); }

if($host != self::VIEW_TV && $this->view_type == 'tv') { $responseUrl = $this->generateRedirectUrl($request,self::VIEW_TV); }

if($responseUrl !== null) { return new RedirectResponse($responseUrl); } return null; }

public function generateRedirectUrl($request,$view) { return 'http://'.$view.$request->getRequestUri(); }

}

Como lo hacemos (pero mejor)

<?phpnamespace Solilokiam\RequestListenerBundle\Twig;

use Symfony\Bundle\TwigBundle\TwigEngine as BaseTwigEngine;use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables;use Symfony\Component\Templating\TemplateNameParserInterface;use Symfony\Component\Config\FileLocatorInterface;use Solilokiam\RequestListenerBundle\Manager\DeviceManager;

class TwigEngine extends BaseTwigEngine{ protected $device_manager;

public function __construct(\Twig_Environment $environment, TemplateNameParserInterface $parser, FileLocatorInterface $locator, DeviceManager $device_manager) { $this->device_manager = $device_manager;

parent::__construct($environment, $parser, $locator); }

public function render($name, array $parameters = array()) { $device = $this->device_manager->getDevice();

$device_template = preg_replace("/^\w+:\w+:/", '$0'.$device.'/', $name); if ($this->exists($device_template)) { $name = $device_template; }

return parent::render($name, $parameters); }}

Como lo hacemos (pero mejor)

Como lo hacemos (pero mejor)

<?php

namespace Solilokiam\FrontBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller{ public function indexAction($name) { return $this->render('SolilokiamFrontBundle:Default:index.html.twig', array('name' => $name)); }}

Misión cumplida

•Faltaría switch entre vistas•Mejor por cookies que por sesión•URL’s y otras cosas configurables

http://www.vayagif.com/169459/claro-que-si-campeon-michael-jackson-estaria-orgulloso-de-ti

Misión cumplida

•Faltaría switch entre vistas•Mejor por cookies que por sesión•URL’s y otras cosas configurables

https://github.com/kbond/ZenstruckMobileBundlehttps://github.com/suncat2000/MobileDetectBundle

http://www.vayagif.com/169459/claro-que-si-campeon-michael-jackson-estaria-orgulloso-de-ti

Ventajas

•Se ajusta mejor a las necesidades•Facilidad de cambiar/hacer cosas especiales•Más óptimo

Inconvenientes

•Mucho trabajo.•Alta posibilidad de liarla parda.•Múltiples url’s para la misma cosa

Responsive Design

•Una plantilla para todos los devices•Una sola hoja de estilos•Un solo javascript*•La presentación se adapta al dispositivo•Soporte a tantos devices como quieras*

Los 3 pilares del Responsive Design

Los 3 pilares del Responsive Design

1.Layout basado en un grid flexible

Los 3 pilares del Responsive Design

1.Layout basado en un grid flexible2.Imágenes y media flexibles

Los 3 pilares del Responsive Design

1.Layout basado en un grid flexible2.Imágenes y media flexibles3.Media Queries

Layout basado en un grid flexible

page { margin: 36px auto; width: 970px;}.blog { margin: 21px 0 0 0; width: 970px;}.blog .main { float: left; padding: 17px;

width: 678px;}.blog .other { float: right;margin: 0 21px 0 0;

width: 271px;}

Normas Básicas

Ningún tamaño fijado.Todo Relativo

•Width x Height•Padding•Margin•Font-Size•...

Regla de Cálculo

objetivo / contexto * 100 = resultado%

objetivo / contexto

.blog .main { float: left; padding: 17px;

width: 678px;}

Un Cálculo de Ejemplo

.blog .main { float: left; padding: 17px;

width: 678px;}

678 / 970 * 100 = 69,896907216495

Un Cálculo de Ejemplo

.blog .main { float: left; padding: 17px;

width: 678px;}

678 / 970 * 100 = 69,896907216495

Un Cálculo de Ejemplo

17 / 970 * 100 = 1,752577319588

678 / 970 * 100 = 69,896907216495

Un Cálculo de Ejemplo

17 / 970 * 100 = 1,752577319588

.blog .main { float: left;

padding: 69,896907216495%; width: 1,752577319588%;}

¿Y qué pasa con la tipografia?

Aplicamos la misma regla de cálculo

h1 { font-size: 24px;}h1 a { font-size: 11px;}

24 / context = ¿?

11/ context = ¿?

¿Y qué pasa con la tipografia?

El tamaño por defecto de la fuente suele ser 16px

¿Y qué pasa con la tipografia?

El tamaño por defecto de la fuente suele ser 16px

Mejor lo seteamos en nuestro fichero de reset

¿Y qué pasa con la tipografia?

El tamaño por defecto de la fuente suele ser 16px

body{font-size:16px;

}

Mejor lo seteamos en nuestro fichero de reset

¿Y qué pasa con la tipografia?

El tamaño por defecto de la fuente suele ser 16px

body{font-size:16px;

}

Mejor lo seteamos en nuestro fichero de reset

body{font-size: 100%;

}

¿Y qué pasa con la tipografia?

El tamaño por defecto de la fuente suele ser 16px

body{font-size:16px;

}

Mejor lo seteamos en nuestro fichero de reset

body{font-size: 100%;

}

http://meyerweb.com/eric/tools/css/reset/

¿Y qué pasa con la tipografia?

Aplicamos la misma regla de calculo

h1 { font-size: 24px;}h1 a { font-size: 11px;}

¿Y qué pasa con la tipografia?

Aplicamos la misma regla de calculo

h1 { font-size: 24px;}h1 a { font-size: 11px;}

24 / 16 = 1.5

¿Y qué pasa con la tipografia?

Aplicamos la misma regla de calculo

h1 { font-size: 24px;}h1 a { font-size: 11px;}

24 / 16 = 1.5

11/ 16 = 0,6875

¿Y qué pasa con la tipografia?

Aplicamos la misma regla de calculo

24 / 16 = 1.5

11/ 16 = 0,6875

h1 { font-size: 1.5em;}h1 a { font-size: 0.6875em;}

Imágenes y media flexibles

Varias Opciones:•Resize•Crop•HTML5 picture tag•Clown Car

Resize

img { max-width: 100%;}

Resize

img,embed,object,video { max-width: 100%;}

Resize

img,embed,object,video { max-width: 100%;}

•La solución más compatible•Ancho de Banda Poco Optimo•Renderizado en navegador poco optimo

Crop

.img_container { overflow: hidden;}.img_container img { display: block; max-width: auto;}

•Igual de compatible que resize•La imagen queda cortada•Ancho de banda poco optimo•Renderizado no excesivamente malo

HTML5 picture tag

<picture alt="Description of image subject."> <source srcset="small.jpg 1x, small-highres.jpg 2x"> <source media="(min-width: 18em)" srcset="med.jpg 1x, med-highres.jpg 2x"><source media="(min-width: 45em)" srcset="large.jpg 1x, large-highres.jpg 2x"> <img src="small.jpg" alt="Description of image subject."> </picture>

•Soporte Webkit (aunque con fallback)•Muy bueno con el ancho de banda•Muy bueno con el navegador

Clown Car<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 329" preserveAspectRatio="xMidYMid meet"><title>Clown Car Technique</title><style>svg { background-size: 100% 100%; background-repeat: no-repeat;}@media screen and (max-width: 400px) { svg { background-image: url(images/small.png"); }}@media screen and (min-width: 401px) and (max-width: 700px) { svg { background-image: url(images/medium.png); }}@media screen and (min-width: 701px) and (max-width: 1000px) { svg { background-image: url(images/big.png); }}@media screen and (min-width: 1001px) { svg { background-image: url(images/huge.png); }}</style></svg>

Clown Car<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 329" preserveAspectRatio="xMidYMid meet"><title>Clown Car Technique</title><style>svg { background-size: 100% 100%; background-repeat: no-repeat;}@media screen and (max-width: 400px) { svg { background-image: url(images/small.png"); }}@media screen and (min-width: 401px) and (max-width: 700px) { svg { background-image: url(images/medium.png); }}@media screen and (min-width: 701px) and (max-width: 1000px) { svg { background-image: url(images/big.png); }}@media screen and (min-width: 1001px) { svg { background-image: url(images/huge.png); }}</style></svg>

•Buen soporte•Buena con el ancho de banda•Buena con el navegador•Requiere de trabajo en el servidor

Media Queries

Desarrollado a partir de los Media Types (CSS 2)

@media screen { body { font-size: 100%; }}@media print { body { font-size: 15pt; }}

Media Queries

@media screen and (min-width: 1024px) { body { font-size: 100%; }}

<link rel="stylesheet" href="wide.css" media="screen and (min-width: 1024px)" />

Media Queries

No solamente podemos filtrar por height o width:

widthheightdevice-widthdevice-heightorientationaspect ratio

color (bit number)color-indexmonochrome(bits x pixel)resolution (dpi)scan(tv progressive o scan)grid

Media Queries

No solamente podemos filtrar por height o width:

widthheightdevice-widthdevice-heightorientationaspect ratio

color (bit number)color-indexmonochrome(bits x pixel)resolution (dpi)scan(tv progressive o scan)grid

@media screen and (min-device-width: 480px) and (orientation: landscape) { ... }

Media Queries

Arreglar todas las cosas que no se acaban de adaptar con Layout basado en un grid flexible

Media Queries

Arreglar todas las cosas que no se acaban de adaptar con Layout basado en un grid flexible

Cambiar tipografiasModificar layout

Mostrar / Ocultar elementos...

Ventajas

•Menos trabajo de crear vistas•Menos posibilidad de liarla•Mejor adaptabilidad ante nuevos devices•Mejor SEO

Inconvenientes

•Mucho trabajo al maquetar•No muy óptimo

Preprocesadores CSS

•Ahorro de muchas horas de maquetación•Cambios Rápidos•Código Estructurado

Assetic los integra perfectamente en nuestros proyectos.

Instalar nodeJS + npm$ npm install less

Symfony

Instalar nodeJS + npm$ npm install less

assetic: filters: cssrewrite: ~ less: node: /usr/local/bin/node node_paths: [/usr/local/lib/node_modules]

Symfony

Instalar nodeJS + npm$ npm install less

assetic: filters: cssrewrite: ~ less: node: /usr/local/bin/node node_paths: [/usr/local/lib/node_modules]

{% block stylesheets %}{% stylesheets 'less/main.less' filter='less' output='css/main.css'%}<link rel="stylesheet" href="{{ asset_url }}">{% endstylesheets %}

Symfony

assetic: filters: cssrewrite: ~ less: node: /usr/local/bin/node node_paths: [/usr/local/lib/node_modules] yui_css: jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"

Symfony

assetic: filters: cssrewrite: ~ less: node: /usr/local/bin/node node_paths: [/usr/local/lib/node_modules] yui_css: jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"

{% block stylesheets %}{% stylesheets 'less/main.less' filter='less,?yui_css’ output='css/main.css'%}<link rel="stylesheet" href="{{ asset_url }}">{% endstylesheets %}

Symfony

./app/console assetic:dump --env=prod --no-debug

Symfony

Variables

@azul: #5B83AD;@azul-claro: (@azul + #111);

#header { color: @azul-claro; }

Variables

@azul: #5B83AD;@azul-claro: (@azul + #111);

#header { color: @azul-claro; }

#header { color: #6c94be; }

Variables

@azul: #5B83AD;@azul-claro: (@azul + #111);

#header { color: @azul-claro; }

#header { color: #6c94be; }

•Scope de las variables igual que CSS•No han de ser declaradas antes de ser utilizadas

Variables

@azul: #5B83AD;@azul-claro: (@azul + #111);

#header { color: @azul-claro; }

#header { color: #6c94be; }

•Scope de las variables igual que CSS•No han de ser declaradas antes de ser utilizadas

@less-mola: "Less Mola";@var: 'less-mola';content: @@var;

Variables

@azul: #5B83AD;@azul-claro: (@azul + #111);

#header { color: @azul-claro; }

#header { color: #6c94be; }

•Scope de las variables igual que CSS•No han de ser declaradas antes de ser utilizadas

@less-mola: "Less Mola";@var: 'less-mola';content: @@var;

content: "Less Mola";

Mixins

.bordered { border-top: dotted 1px black; border-bottom: solid 2px black;}#menu a { color: #111; .bordered;}

Mixins

.bordered { border-top: dotted 1px black; border-bottom: solid 2px black;}#menu a { color: #111; .bordered;}

#menu a { color: #111; border-top: dotted 1px black; border-bottom: solid 2px black;}

Mixins

.bordered { border-top: dotted 1px black; border-bottom: solid 2px black;}#menu a { color: #111; .bordered;}

#menu a { color: #111; border-top: dotted 1px black; border-bottom: solid 2px black;}

.border-radius (@radius: 5px) { border-radius: @radius; -moz-border-radius: @radius; -webkit-border-radius: @radius;}#header { .border-radius(4px);}.button { .border-radius;}

Mixins

.bordered { border-top: dotted 1px black; border-bottom: solid 2px black;}#menu a { color: #111; .bordered;}

#menu a { color: #111; border-top: dotted 1px black; border-bottom: solid 2px black;}

.border-radius (@radius: 5px) { border-radius: @radius; -moz-border-radius: @radius; -webkit-border-radius: @radius;}#header { .border-radius(4px);}.button { .border-radius;}

#header{ border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px;}

.button { border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;}

Mixins

Multiples Parametros separados por semicolon

Mixins

Multiples Parametros separados por semicolon

Less permite sobrecarga en los mixins

Mixins

Multiples Parametros separados por semicolon

Less permite sobrecarga en los mixins

.test_mixin(@width) { width: @width;}.test_mixin(@width; @color:#000000) { width-2: @width; color: @padding;}.test_mixin(@width; @color; @margin: 2) { width-3: @color; color-3: @padding; margin: @margin @margin @margin @margin;}.test div { .test_mixin(15px);}

Mixins

Multiples Parametros separados por semicolon

Less permite sobrecarga en los mixins

.test_mixin(@width) { width: @width;}.test_mixin(@width; @color:#000000) { width-2: @width; color: @padding;}.test_mixin(@width; @color; @margin: 2) { width-3: @color; color-3: @padding; margin: @margin @margin @margin @margin;}.test div { .test_mixin(15px);}

.test div { width: 15px; width-2: 15px; color: #000000;}

Mixins

.margin_mixin(@top; @right; @down; @left) { margin: @arguments;}.margin_mixin(2px; 5px;2px;5px);

Mixins

.margin_mixin(@top; @right; @down; @left) { margin: @arguments;}.margin_mixin(2px; 5px;2px;5px);

margin: 2px 5px 2px 5px;

!important

.test_mixin (@a: 0) { margin: @a; padding: @a;}.no_importante { .test_mixin(1); }.muy_importante { .test_mixin(2) !important; }

!important

.test_mixin (@a: 0) { margin: @a; padding: @a;}.no_importante { .test_mixin(1); }.muy_importante { .test_mixin(2) !important; }

.no_importante { margin: 1px; padding: 1px;}.muy_importante { margin: 2px !important; padding: 2px !important;}

Nested Rules

#header { color: black; }#header .navigation { font-size: 12px;}#header .logo { width: 300px;}#header .logo:hover { text-decoration: none;}

Nested Rules

#header { color: black; }#header .navigation { font-size: 12px;}#header .logo { width: 300px;}#header .logo:hover { text-decoration: none;}

#header { color: black; .navigation { font-size: 12px } .logo { width: 300px; &:hover { text-decoration: none } }}

Operations

•Afectan a numeros y colores•Tienen que ir entre paréntesis

Operations

•Afectan a numeros y colores•Tienen que ir entre paréntesis@context: 970

.blog .main { float: left;padding: percentage((679px / @context));

width: percentage((17px / @context));

}

Operations

•Afectan a numeros y colores•Tienen que ir entre paréntesis@context: 970

.blog .main { float: left;padding: percentage((679px / @context));

width: percentage((17px / @context));

}

.blog .main { float: left;

padding: 69,896907216495%; width: 1,752577319588%;}

Importing

@import "test.css";

Importing

@import "test.css"; @import "library.less";

Importing

@import "test.css"; @import "library.less";

@imported-color: red;h1 { color: green; }

@import "library.less" screen and (max-width: 400px);@import "library.less";

.class { color: @importedColor;}

Importing

@import "test.css"; @import "library.less";

@imported-color: red;h1 { color: green; }

@import "library.less" screen and (max-width: 400px);@import "library.less";

.class { color: @importedColor;}

@media screen and (max-width: 400px) { h1 { color: green; }}

h1 { color: green; }.class { // Use imported variable color: #ff0000;}

Funciones

escape(@string); // URL encodes a stringe(@string); // escape string content%(@string, values...); // formats a string

ceil(@number); // rounds up to an integerfloor(@number); // rounds down to an integerpercentage(@number); // converts to a %, e.g. 0.5 -> 50%round(number, [places: 0]);// rounds a number to a number of places

saturate(@color, 10%); // return a color 10% points *more* saturated

desaturate(@color, 10%);// return a color 10% points *less* saturated

lighten(@color, 10%); // return a color 10% points *lighter*

darken(@color, 10%); // return a color 10% points *darker*

Pattern Matching

.mixin (dark; @color) { color: darken(@color, 10%);}.mixin (light; @color) { color: lighten(@color, 10%);}.mixin (@_; @color) { display: block;}

Pattern Matching

.mixin (dark; @color) { color: darken(@color, 10%);}.mixin (light; @color) { color: lighten(@color, 10%);}.mixin (@_; @color) { display: block;}

@switch: light;

.class { .mixin(@switch; #888);}

Pattern Matching

.mixin (dark; @color) { color: darken(@color, 10%);}.mixin (light; @color) { color: lighten(@color, 10%);}.mixin (@_; @color) { display: block;}

@switch: light;

.class { .mixin(@switch; #888);}

.class { color: #a2a2a2; display: block;}

Guarded Params

.mixin (@a) when (lightness(@a) >= 50%) { background-color: black;}.mixin (@a) when (lightness(@a) < 50%) { background-color: white;}.mixin (@a) { color: @a;}

Guarded Params

.mixin (@a) when (lightness(@a) >= 50%) { background-color: black;}.mixin (@a) when (lightness(@a) < 50%) { background-color: white;}.mixin (@a) { color: @a;}

.class1 { .mixin(#ddd) }

.class2 { .mixin(#555) }

Guarded Params

.mixin (@a) when (lightness(@a) >= 50%) { background-color: black;}.mixin (@a) when (lightness(@a) < 50%) { background-color: white;}.mixin (@a) { color: @a;}

.class1 { .mixin(#ddd) }

.class2 { .mixin(#555) }

.class1 { background-color: black; color: #ddd;}.class2 { background-color: white; color: #555;}

Comentarios

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

Comentarios

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

Comentarios

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

//Hola soy un comentario que mejor no salga en produccion.test_class { color: #008866 }

Comentarios

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

/* Hola soy un comentario la mar de interesante */.test_class { color: #008866 }

//Hola soy un comentario que mejor no salga en produccion.test_class { color: #008866 }

.test_class { color: #008866 }

Quiero saber más.

http://lesscss.org http://twitter.github.io/bootstrap/

http://lesshat.com/

¿Qué pasa con SASS?

•Más completo que less•Peor documentación•Mismos principios•Hecho en ruby•Ultimamente menos activo

http://sass-lang.com/

http://compass-style.org/

http://foundation.zurb.com/

Resumiendo

•Adaptad vuestra aplicación a varios devices•Analizad vuestras necesidades•Aplicad la técnica que mejor os vaya•Utilizad preprocesadores de CSS