1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-23 05:50:54 +01:00

Add a LESS linter.

Summary: This class provides an adapter for [[https://github.com/less/less.js/ | lessc]].

Test Plan: Wrote and executed unit tests.

Reviewers: chad, #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: epriestley, Korvin

Differential Revision: https://secure.phabricator.com/D8992
This commit is contained in:
Joshua Spence 2014-05-06 06:50:44 -07:00 committed by epriestley
parent 9a13c87ac4
commit 88ff113f92
96 changed files with 3860 additions and 0 deletions

View file

@ -87,6 +87,8 @@ phutil_register_library_map(array(
'ArcanistJSONLintLinter' => 'lint/linter/ArcanistJSONLintLinter.php',
'ArcanistJSONLintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php',
'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
'ArcanistLesscLinter' => 'lint/linter/ArcanistLesscLinter.php',
'ArcanistLesscLinterTestCase' => 'lint/linter/__tests__/ArcanistLesscLinterTestCase.php',
'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php',
'ArcanistLintConsoleRenderer' => 'lint/renderer/ArcanistLintConsoleRenderer.php',
'ArcanistLintEngine' => 'lint/engine/ArcanistLintEngine.php',
@ -254,6 +256,8 @@ phutil_register_library_map(array(
'ArcanistJSONLintLinter' => 'ArcanistExternalLinter',
'ArcanistJSONLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLesscLinter' => 'ArcanistExternalLinter',
'ArcanistLesscLinterTestCase' => 'ArcanistArcanistLinterTestCase',
'ArcanistLiberateWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistLintConsoleRenderer' => 'ArcanistLintRenderer',
'ArcanistLintJSONRenderer' => 'ArcanistLintRenderer',

View file

@ -0,0 +1,160 @@
<?php
/**
* A linter for LESSCSS files.
*
* This linter uses [[https://github.com/less/less.js | lessc]] to detect
* errors and potential problems in [[http://lesscss.org/ | LESS]] code.
*/
final class ArcanistLesscLinter extends ArcanistExternalLinter {
const LINT_RUNTIME_ERROR = 1;
const LINT_ARGUMENT_ERROR = 2;
const LINT_FILE_ERROR = 3;
const LINT_NAME_ERROR = 4;
const LINT_OPERATION_ERROR = 5;
const LINT_PARSE_ERROR = 6;
const LINT_SYNTAX_ERROR = 7;
public function getLinterName() {
return 'LESSC';
}
public function getLinterConfigurationName() {
return 'lessc';
}
public function getLinterConfigurationOptions() {
return parent::getLinterConfigurationOptions() + array(
'lessc.strict-math' => 'optional bool',
'lessc.strict-units' => 'optional bool',
);
}
public function getLintNameMap() {
return array(
self::LINT_RUNTIME_ERROR => pht('Runtime Error'),
self::LINT_ARGUMENT_ERROR => pht('Argument Error'),
self::LINT_FILE_ERROR => pht('File Error'),
self::LINT_NAME_ERROR => pht('Name Error'),
self::LINT_OPERATION_ERROR => pht('Operation Error'),
self::LINT_PARSE_ERROR => pht('Parse Error'),
self::LINT_SYNTAX_ERROR => pht('Syntax Error'),
);
}
public function getDefaultBinary() {
return 'lessc';
}
public function getCacheVersion() {
list($stdout) = execx('%C --version', $this->getExecutableCommand());
$matches = array();
if (preg_match('/^lessc (?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
$version = $matches['version'];
} else {
return false;
}
}
public function getInstallInstructions() {
return pht('Install lessc using `npm install -g lessc`.');
}
public function shouldExpectCommandErrors() {
return true;
}
public function supportsReadDataFromStdin() {
// Technically `lessc` can read data from standard input however, when doing
// so, relative imports cannot be resolved. Therefore, this functionality is
// disabled.
return false;
}
public function getReadDataFromStdinFilename() {
return '-';
}
protected function getMandatoryFlags() {
return array(
'--lint',
'--no-color',
'--strict-math='.
($this->getConfig('lessc.strict-math') ? 'on' : 'off'),
'--strict-units='.
($this->getConfig('lessc.strict-units') ? 'on' : 'off'));
}
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
$lines = phutil_split_lines($stderr, false);
$messages = array();
foreach ($lines as $line) {
$matches = null;
$match = preg_match(
'/^(?P<name>\w+): (?P<description>.+) ' .
'in (?P<path>.+|-) ' .
'on line (?P<line>\d+), column (?P<column>\d+):$/',
$line,
$matches);
if ($match) {
switch ($matches['name']) {
case 'RuntimeError':
$code = self::LINT_RUNTIME_ERROR;
break;
case 'ArgumentError':
$code = self::LINT_ARGUMENT_ERROR;
break;
case 'FileError':
$code = self::LINT_FILE_ERROR;
break;
case 'NameError':
$code = self::LINT_NAME_ERROR;
break;
case 'OperationError':
$code = self::LINT_OPERATION_ERROR;
break;
case 'ParseError':
$code = self::LINT_PARSE_ERROR;
break;
case 'SyntaxError':
$code = self::LINT_SYNTAX_ERROR;
break;
default:
throw new RuntimeException(pht(
'Unrecognized lint message code "%s".',
$code));
}
$code = $this->getLintCodeFromLinterConfigurationKey($matches['name']);
$message = new ArcanistLintMessage();
$message->setPath($path);
$message->setLine($matches['line']);
$message->setChar($matches['column']);
$message->setCode($this->getLintMessageFullCode($code));
$message->setSeverity($this->getLintMessageSeverity($code));
$message->setName($this->getLintMessageName($code));
$message->setDescription(ucfirst($matches['description']));
$messages[] = $message;
}
}
if ($err && !$messages) {
return false;
}
return $messages;
}
}

View file

@ -0,0 +1,12 @@
<?php
final class ArcanistLesscLinterTestCase
extends ArcanistArcanistLinterTestCase {
public function testLesscLinter() {
$this->executeTestsInDirectory(
dirname(__FILE__).'/lessc/',
new ArcanistLesscLinter());
}
}

View file

@ -0,0 +1,99 @@
#yelow {
#short {
color: #fea;
}
#long {
color: #ffeeaa;
}
#rgba {
color: rgba(255, 238, 170, 0.1);
}
#argb {
color: argb(rgba(255, 238, 170, 0.1));
}
}
#blue {
#short {
color: #00f;
}
#long {
color: #0000ff;
}
#rgba {
color: rgba(0, 0, 255, 0.1);
}
#argb {
color: argb(rgba(0, 0, 255, 0.1));
}
}
#alpha #hsla {
color: hsla(11, 20%, 20%, 0.6);
}
#overflow {
.a { color: (#111111 - #444444); } // #000000
.b { color: (#eee + #fff); } // #ffffff
.c { color: (#aaa * 3); } // #ffffff
.d { color: (#00ee00 + #009900); } // #00ff00
.e { color: rgba(-99.9, 31.4159, 321, 0.42); }
}
#grey {
color: rgb(200, 200, 200);
}
#333333 {
color: rgb(20%, 20%, 20%);
}
#808080 {
color: hsl(50, 0%, 50%);
}
#00ff00 {
color: hsl(120, 100%, 50%);
}
.lightenblue {
color: lighten(blue, 10%);
}
.darkenblue {
color: darken(blue, 10%);
}
.unknowncolors {
color: blue2;
border: 2px solid superred;
}
.transparent {
color: transparent;
background-color: rgba(0, 0, 0, 0);
}
#alpha {
@colorvar: rgba(150, 200, 150, 0.7);
#fromvar {
opacity: alpha(@colorvar);
}
#short {
opacity: alpha(#aaa);
}
#long {
opacity: alpha(#bababa);
}
#rgba {
opacity: alpha(rgba(50, 120, 95, 0.2));
}
#hsl {
opacity: alpha(hsl(120, 100%, 50%));
}
}
#percentage {
color: red(rgb(100%, 0, 0));
border-color: rgba(100%, 0, 0, 50%);
}
~~~~~~~~~~

View file

@ -0,0 +1,84 @@
/******************\
* *
* Comment Header *
* *
\******************/
/*
Comment
*/
/*
* Comment Test
*
* - cloudhead (http://cloudhead.net)
*
*/
////////////////
@var: "content";
////////////////
/* Colors
* ------
* #EDF8FC (background blue)
* #166C89 (darkest blue)
*
* Text:
* #333 (standard text) // A comment within a comment!
* #1F9EC9 (standard link)
*
*/
/* @group Variables
------------------- */
#comments /* boo *//* boo again*/,
//.commented_out1
//.commented_out2
//.commented_out3
.comments //end of comments1
//end of comments2
{
/**/ // An empty comment
color: red; /* A C-style comment */ /* A C-style comment */
background-color: orange; // A little comment
font-size: 12px;
/* lost comment */ content: @var;
border: 1px solid black;
// padding & margin //
padding: 0; // }{ '"
margin: 2em;
} //
/* commented out
#more-comments {
color: grey;
}
*/
.selector /* .with */, .lots, /* of */ .comments {
color: grey, /* blue */ orange;
-webkit-border-radius: 2px /* webkit only */;
-moz-border-radius: (2px * 4) /* moz only with operation */;
}
.mixin_def_with_colors(@a: white, // in
@b: 1px //put in @b - causes problems! --->
) // the
when (@a = white) {
.test {
color: @b;
}
}
.mixin_def_with_colors();
#last { color: blue }
//
/* *//* { *//* *//* *//* */#div { color:#A33; }/* } */
~~~~~~~~~~

View file

@ -0,0 +1,130 @@
.comma-delimited {
text-shadow: -1px -1px 1px red, 6px 5px 5px yellow;
-moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset,
0pt 4px 6px rgba(255, 255, 255, 0.4) inset;
-webkit-transform: rotate(-0.0000000001deg);
}
@font-face {
font-family: Headline;
unicode-range: U+??????, U+0???, U+0-7F, U+A5;
}
.other {
-moz-transform: translate(0, 11em) rotate(-90deg);
transform: rotateX(45deg);
}
.item[data-cra_zy-attr1b-ut3=bold] {
font-weight: bold;
}
p:not([class*="lead"]) {
color: black;
}
input[type="text"].class#id[attr=32]:not(1) {
color: white;
}
div#id.class[a=1][b=2].class:not(1) {
color: white;
}
ul.comma > li:not(:only-child)::after {
color: white;
}
ol.comma > li:nth-last-child(2)::after {
color: white;
}
li:nth-child(4n+1),
li:nth-child(-5n),
li:nth-child(-n+2) {
color: white;
}
a[href^="http://"] {
color: black;
}
a[href$="http://"] {
color: black;
}
form[data-disabled] {
color: black;
}
p::before {
color: black;
}
#issue322 {
-webkit-animation: anim2 7s infinite ease-in-out;
}
@-webkit-keyframes frames {
0% { border: 1px }
5.5% { border: 2px }
100% { border: 3px }
}
@keyframes fontbulger1 {
to {
font-size: 15px;
}
from,to {
font-size: 12px;
}
0%,100% {
font-size: 12px;
}
}
.units {
font: 1.2rem/2rem;
font: 8vw/9vw;
font: 10vh/12vh;
font: 12vm/15vm;
font: 12vmin/15vmin;
font: 1.2ch/1.5ch;
}
@supports ( box-shadow: 2px 2px 2px black ) or
( -moz-box-shadow: 2px 2px 2px black ) {
.outline {
box-shadow: 2px 2px 2px black;
-moz-box-shadow: 2px 2px 2px black;
}
}
@-x-document url-prefix(""github.com"") {
h1 {
color: red;
}
}
@viewport {
font-size: 10px;
}
@namespace foo url(http://www.example.com);
foo|h1 { color: blue; }
foo|* { color: yellow; }
|h1 { color: red; }
*|h1 { color: green; }
h1 { color: green; }
.upper-test {
UpperCaseProperties: allowed;
}
@host {
div {
display: block;
}
}
::distributed(input::placeholder) {
color: #b3b3b3;
}
.shadow ^ .dom,
body ^^ .shadow {
display: done;
}
~~~~~~~~~~

View file

@ -0,0 +1,34 @@
@ugly: fuchsia;
.escape\|random\|char {
color: red;
}
.mixin\!tUp {
font-weight: bold;
}
// class="404"
.\34 04 {
background: red;
strong {
color: @ugly;
.mixin\!tUp;
}
}
.trailingTest\+ {
color: red;
}
/* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */
\62\6c\6f \63 \6B \0071 \000075o\74 e {
color: silver;
}
[ng\:cloak],
ng\:form {
display: none;
}
~~~~~~~~~~

View file

@ -0,0 +1,65 @@
.light when (lightness(@a) > 50%) {
color: green;
}
.dark when (lightness(@a) < 50%) {
color: orange;
}
@a: #ddd;
.see-the {
@a: #444; // this mirrors what mixins do - they evaluate guards at the point of execution
.light();
.dark();
}
.hide-the {
.light();
.dark();
}
.multiple-conditions-1 when (@b = 1), (@c = 2), (@d = 3) {
color: red;
}
.multiple-conditions-2 when (@b = 1), (@c = 2), (@d = 2) {
color: blue;
}
@b: 2;
@c: 3;
@d: 3;
.inheritance when (@b = 2) {
.test {
color: black;
}
&:hover {
color: pink;
}
.hideme when (@b = 1) {
color: green;
}
& when (@b = 1) {
hideme: green;
}
}
.hideme when (@b = 1) {
.test {
color: black;
}
&:hover {
color: pink;
}
.hideme when (@b = 1) {
color: green;
}
}
& when (@b = 1) {
.hideme {
color: red;
}
}
~~~~~~~~~~

View file

@ -0,0 +1,109 @@
@charset "utf-8";
div { color: black; }
div { width: 99%; }
* {
min-width: 45em;
}
h1, h2 > a > p, h3 {
color: none;
}
div.class {
color: blue;
}
div#id {
color: green;
}
.class#id {
color: purple;
}
.one.two.three {
color: grey;
}
@media print {
* {
font-size: 3em;
}
}
@media screen {
* {
font-size: 10px;
}
}
@font-face {
font-family: 'Garamond Pro';
}
a:hover, a:link {
color: #999;
}
p, p:first-child {
text-transform: none;
}
q:lang(no) {
quotes: none;
}
p + h1 {
font-size: +2.2em;
}
#shorthands {
border: 1px solid #000;
font: 12px/16px Arial;
font: 100%/16px Arial;
margin: 1px 0;
padding: 0 auto;
}
#more-shorthands {
margin: 0;
padding: 1px 0 2px 0;
font: normal small/20px 'Trebuchet MS', Verdana, sans-serif;
font: 0/0 a;
border-radius: 5px / 10px;
}
.misc {
-moz-border-radius: 2px;
display: -moz-inline-stack;
width: .1em;
background-color: #009998;
background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue));
margin: ;
.nested-multiple {
multiple-semi-colons: yes;;;;;;
};
filter: alpha(opacity=100);
width: auto\9;
}
#important {
color: red !important;
width: 100%!important;
height: 20px ! important;
}
.def-font(@name) {
@font-face {
font-family: @name
}
}
.def-font(font-a);
.def-font(font-b);
.æøå {
margin: 0;
}
~~~~~~~~~~

View file

@ -0,0 +1,104 @@
@ruleset: {
color: black;
background: white;
};
@a: 1px;
.wrap-mixin(@ruleset) {
@a: hidden and if you see this in the output its a bug;
@b: visible;
@d: magic-frame; // same behaviour as mixin calls - falls back to this frame
.wrap-selector {
@c: visible;
@ruleset();
visible-one: @b;
visible-two: @c;
}
};
.wrap-mixin({
color: black;
one: @a;
@b: hidden and if you see this in the output its a bug;
@c: hidden and if you see this in the output its a bug;
four: @d;
});
.wrap-mixin(@ruleset: {
color: red;
});
.wrap-mixin(@ruleset);
.desktop-and-old-ie(@rules) {
@media screen and (min-width: 1200) { @rules(); }
html.lt-ie9 & { @rules(); }
}
header {
background: blue;
.desktop-and-old-ie({
background: red;
});
}
.wrap-mixin-calls-wrap(@ruleset) {
.wrap-mixin(@ruleset);
};
.wrap-mixin({
test: extra-wrap;
.wrap-mixin-calls-wrap({
test: wrapped-twice;
});
});
.wrap-mixin({
test-func: unit(90px);
test-arithmetic: unit((9+9), px);
});
// without mixins
@ruleset-2: {
b: 1;
};
.without-mixins {
@ruleset-2();
}
@my-ruleset: {
.my-selector {
@media tv {
background-color: black;
}
}
};
@media (orientation:portrait) {
@my-ruleset();
.wrap-media-mixin({
@media tv {
.triple-wrapped-mq {
triple: true;
}
}
});
}
.wrap-media-mixin(@ruleset) {
@media widescreen {
@media print {
@ruleset();
}
@ruleset();
}
@ruleset();
}
// unlocking mixins
@my-mixins: {
.mixin() {
test: test;
}
};
@my-mixins();
.a {
.mixin();
}
~~~~~~~~~~

View file

@ -0,0 +1 @@
~~~~~~~~~~

View file

@ -0,0 +1,8 @@
.a {
error: (1px + 3em);
}
~~~~~~~~~~
error:2:3
~~~~~~~~~~
~~~~~~~~~~
{"config":{"lessc.strict-units":true}}

View file

@ -0,0 +1,8 @@
.a {
error: ((1px * 2px) + (3em * 3px));
}
~~~~~~~~~~
error:2:3
~~~~~~~~~~
~~~~~~~~~~
{"config":{"lessc.strict-units":true}}

View file

@ -0,0 +1,6 @@
@keyframes @name {
50% {width: 20px;}
}
~~~~~~~~~~
error:2:12

View file

@ -0,0 +1,3 @@
@@demo: "hi";
~~~~~~~~~~
error:1:1

View file

@ -0,0 +1,5 @@
.test {
color: color("NOT A COLOR");
}
~~~~~~~~~~
error:2:10

View file

@ -0,0 +1,3 @@
#gaga /* Comment */ span { color: red }
~~~~~~~~~~
error:1:21

View file

@ -0,0 +1,6 @@
selector when (default()) {
color: red;
}
~~~~~~~~~~
error:2:16

View file

@ -0,0 +1,8 @@
@a: {
b: 1;
};
.a {
a: @a;
}
~~~~~~~~~~
error:5:3

View file

@ -0,0 +1,8 @@
@a: {
b: 1;
};
.a {
a: @a();
}
~~~~~~~~~~
error:5:3

View file

@ -0,0 +1,6 @@
@a: {
b: 1;
};
@a();
~~~~~~~~~~
error:2:3

View file

@ -0,0 +1,7 @@
.mixin-definition(@a: {
b: 1;
}) {
@a();
}
~~~~~~~~~~
error:1:18

View file

@ -0,0 +1,6 @@
.mixin-definition(@b) {
@a();
}
.mixin-definition({color: red;});
~~~~~~~~~~
error:4:1

View file

@ -0,0 +1,7 @@
.a {
b: {
color: red;
};
}
~~~~~~~~~~
error:2:3

View file

@ -0,0 +1,8 @@
.a {
error: (1px / 3em);
}
~~~~~~~~~~
error:2:3
~~~~~~~~~~
~~~~~~~~~~
{"config":{"lessc.strict-units":true}}

View file

@ -0,0 +1,5 @@
:extend(.a all) {
property: red;
}
~~~~~~~~~~
error:1:17

View file

@ -0,0 +1,5 @@
.a:extend(.b all).c {
property: red;
}
~~~~~~~~~~
error:1:21

View file

@ -0,0 +1,8 @@
.a {
color: green;
// tests line number for import reference is correct
}
@import "file-does-not-exist.less";
~~~~~~~~~~
error:6:1

View file

@ -0,0 +1,3 @@
@import "this-statement-is-invalid.less"
~~~~~~~~~~
error:1:1

View file

@ -0,0 +1,5 @@
.scope {
var: `this.foo.toJS()`;
}
~~~~~~~~~~
error:2:27

View file

@ -0,0 +1,5 @@
.scope {
@a: `@{b}`;
}
~~~~~~~~~~
error:2:15

View file

@ -0,0 +1,8 @@
.mixin(@a : 4, @b : 3, @c: 2) {
will: fail;
}
.mixin-test {
.mixin(@a: 5; @b: 6, @c: 7);
}
~~~~~~~~~~
error:5:30

View file

@ -0,0 +1,8 @@
.mixin(@a : 4, @b : 3, @c: 2) {
will: fail;
}
.mixin-test {
.mixin(@a: 5, @b: 6; @c: 7);
}
~~~~~~~~~~
error:5:26

View file

@ -0,0 +1,13 @@
.error-is-further-on() {
}
.pad-here-to-reproduce-error-in() {
}
.the-import-subfolder-test() {
}
.mixin-not-defined();
~~~~~~~~~~
error:11:1

View file

@ -0,0 +1,8 @@
@saxofon:trumpete;
.mixin(saxofon) {
}
.mixin(@saxofon);
~~~~~~~~~~
error:6:1

View file

@ -0,0 +1,8 @@
@saxofon:trumpete;
.mixin(@a, @b) {
}
.mixin(@a: @saxofon);
~~~~~~~~~~
error:6:1

View file

@ -0,0 +1,11 @@
.something {
& {
.a {value: a}
}
& {
.b {.a} // was Err. before 1.6.2
}
}
~~~~~~~~~~
error:7:13

View file

@ -0,0 +1,11 @@
guard-default-func-conflict {
.m(@x, 1) {}
.m(@x, 2) when (default()) {}
.m(@x, 2) when (default()) {}
.m(1, 1);
.m(1, 2);
}
~~~~~~~~~~
error:8:5

View file

@ -0,0 +1,11 @@
guard-default-func-conflict {
.m(1) {}
.m(@x) when not(default()) {}
.m(@x) when (@x = 3) and (default()) {}
.m(2);
.m(3);
}
~~~~~~~~~~
error:8:5

View file

@ -0,0 +1,11 @@
guard-default-func-conflict {
.m(1) {}
.m(@x) when not(default()) {}
.m(@x) when not(default()) {}
.m(1);
.m(2);
}
~~~~~~~~~~
error:8:5

View file

@ -0,0 +1,6 @@
@ie8: true;
.a when (@ie8 = true),
.b {
}
~~~~~~~~~~
error:3:1

View file

@ -0,0 +1,6 @@
@ie8: true;
.a,
.b when (@ie8 = true) {
}
~~~~~~~~~~
error:3:23

View file

@ -0,0 +1,12 @@
/* Test */
#blah {
// blah
}
.a {
error: (1px * 1em);
}
~~~~~~~~~~
error:6:3
~~~~~~~~~~
~~~~~~~~~~
{"config":{"lessc.strict-units":true}}

View file

@ -0,0 +1,5 @@
.a {
something: (12 (13 + 5 -23) + 5);
}
~~~~~~~~~~
error:2:18

View file

@ -0,0 +1,5 @@
.a {
something: (12 * (13 + 5 -23));
}
~~~~~~~~~~
error:2:28

View file

@ -0,0 +1,5 @@
.a {
something: (12 + (13 + 10 -23));
}
~~~~~~~~~~
error:2:29

View file

@ -0,0 +1,6 @@
body {
background-color: #fff;
}
}
~~~~~~~~~~
error:4:1

View file

@ -0,0 +1,7 @@
@media (extra: bracket)) {
body {
background-color: #fff;
}
}
~~~~~~~~~~
error:1:24

View file

@ -0,0 +1,4 @@
body {
background-color: #fff;
~~~~~~~~~~
error:1:6

View file

@ -0,0 +1,7 @@
@media (missing: bracket {
body {
background-color: #fff;
}
}
~~~~~~~~~~
error:1:8

View file

@ -0,0 +1,5 @@
.a {
error: calc(1 %);
}
~~~~~~~~~~
error:2:3

View file

@ -0,0 +1,5 @@
a {
* : 1;
}
~~~~~~~~~~
error:2:5

View file

@ -0,0 +1,5 @@
.test {
display/*/: block; /*sorry for IE5*/
}
~~~~~~~~~~
error:2:3

View file

@ -0,0 +1,6 @@
.a() {
prop:1;
}
.a();
~~~~~~~~~~
error:2:3

View file

@ -0,0 +1,6 @@
prop:1;
.a {
prop:1;
}
~~~~~~~~~~
error:1:1

View file

@ -0,0 +1,3 @@
a {outline-@{color}: green}
~~~~~~~~~~
error:1:12

View file

@ -0,0 +1,3 @@
@bodyColor: darken(@bodyColor, 30%);
~~~~~~~~~~
error:1:20

View file

@ -0,0 +1,5 @@
.a {
a: svg-gradient(horizontal, black, white);
}
~~~~~~~~~~
error:2:6

View file

@ -0,0 +1,5 @@
.a {
a: svg-gradient(to bottom, black, orange, 45%, white);
}
~~~~~~~~~~
error:2:6

View file

@ -0,0 +1,5 @@
.a {
a: svg-gradient(black, orange);
}
~~~~~~~~~~
error:2:6

View file

@ -0,0 +1,8 @@
.a {
font-size: unit(80/16,rem);
}
~~~~~~~~~~
error:2:14
~~~~~~~~~~
~~~~~~~~~~
{"config":{"lessc.strict-math":true}}

View file

@ -0,0 +1,92 @@
//very simple chaining
.a {
color: black;
}
.b:extend(.a) {}
.c:extend(.b) {}
//very simple chaining, ordering not important
.d:extend(.e) {}
.e:extend(.f) {}
.f {
color: black;
}
//extend with all
.g.h {
color: black;
}
.i.j:extend(.g all) {
color: white;
}
.k:extend(.i all) {}
//extend multi-chaining
.l {
color: black;
}
.m:extend(.l){}
.n:extend(.m){}
.o:extend(.n){}
.p:extend(.o){}
.q:extend(.p){}
.r:extend(.q){}
.s:extend(.r){}
.t:extend(.s){}
// self referencing is ignored
.u {color: black;}
.v.u.v:extend(.u all){}
// circular reference because the new extend product will match the existing extend
.w:extend(.w) {color: black;}
.v.w.v:extend(.w all){}
// classic circular references
.x:extend(.z) {
color: x;
}
.y:extend(.x) {
color: y;
}
.z:extend(.y) {
color: z;
}
//very simple chaining, but with the extend inside the ruleset
.va {
color: black;
}
.vb {
&:extend(.va);
color: white;
}
.vc {
&:extend(.vb);
}
// media queries - dont extend outside, do extend inside
@media tv {
.ma:extend(.a,.b,.c,.d,.e,.f,.g,.h,.i,.j,.k,.l,.m,.n,.o,.p,.q,.r,.s,.t,.u,.v,.w,.x,.y,.z,.md) {
color: black;
}
.md {
color: white;
}
@media plasma {
.me, .mf {
&:extend(.mb,.md);
background: red;
}
}
}
.mb:extend(.ma) {};
.mc:extend(.mb) {};
~~~~~~~~~~

View file

@ -0,0 +1,20 @@
.clearfix {
*zoom: 1;
&:after {
content: '';
display: block;
clear: both;
height: 0;
}
}
.foo {
&:extend(.clearfix all);
color: red;
}
.bar {
&:extend(.clearfix all);
color: blue;
}
~~~~~~~~~~

View file

@ -0,0 +1,47 @@
.replace.replace,
.c.replace + .replace {
.replace,
.c {
prop: copy-paste-replace;
}
}
.rep_ace:extend(.replace.replace .replace) {}
.a .b .c {
prop: not_effected;
}
.a {
prop: is_effected;
.b {
prop: not_effected;
}
.b.c {
prop: not_effected;
}
}
.c, .a {
.b, .a {
.a, .c {
prop: not_effected;
}
}
}
.effected {
&:extend(.a);
&:extend(.b);
&:extend(.c);
}
.e {
&& {
prop: extend-double;
&:hover {
hover: not-extended;
}
}
}
.dbl:extend(.e.e) {}
~~~~~~~~~~

View file

@ -0,0 +1,25 @@
.ext1 .ext2 {
background: black;
}
@media tv {
.ext1 .ext3 {
color: white;
}
.tv-lowres :extend(.ext1 all) {
background: blue;
}
@media hires {
.ext1 .ext4 {
color: green;
}
.tv-hires :extend(.ext1 all) {
background: red;
}
}
}
.all:extend(.ext1 all) {
}
~~~~~~~~~~

View file

@ -0,0 +1,66 @@
.sidebar {
width: 300px;
background: red;
.box {
background: #FFF;
border: 1px solid #000;
margin: 10px 0;
}
}
.sidebar2 {
&:extend(.sidebar all);
background: blue;
}
.type1 {
.sidebar3 {
&:extend(.sidebar all);
background: green;
}
}
.type2 {
&.sidebar4 {
&:extend(.sidebar all);
background: red;
}
}
.button {
color: black;
&:hover {
color: white;
}
}
.submit {
&:extend(.button);
&:hover:extend(.button:hover) {}
}
.nomatch {
&:hover:extend(.button :hover) {}
}
.button2 {
:hover {
nested: white;
}
}
.button2 :hover {
notnested: black;
}
.nomatch :extend(.button2:hover) {}
.amp-test-a,
.amp-test-b {
.amp-test-c &.amp-test-d&.amp-test-e {
.amp-test-f&+&.amp-test-g:extend(.amp-test-h) {}
}
}
.amp-test-h {
test: extended by masses of selectors;
}
~~~~~~~~~~

View file

@ -0,0 +1,100 @@
.error {
border: 1px #f00;
background: #fdd;
}
.error.intrusion {
font-size: 1.3em;
font-weight: bold;
}
.intrusion .error {
display: none;
}
.badError:extend(.error all) {
border-width: 3px;
}
.foo .bar, .foo .baz {
display: none;
}
.ext1 .ext2
:extend(.foo all) {
}
.ext3:extend(.foo all),
.ext4:extend(.foo all) {
}
div.ext5,
.ext6 > .ext5 {
width: 100px;
}
.should-not-exist-in-output,
.ext7:extend(.ext5 all) {
}
.ext {
test: 1;
}
// same as
// .a .c:extend(.ext all)
// .b .c:extend(.ext all)
// .a .c .d
// .b .c .d
.a, .b {
test: 2;
.c:extend(.ext all) {
test: 3;
.d {
test: 4;
}
}
}
.replace.replace,
.c.replace + .replace {
.replace,
.c {
prop: copy-paste-replace;
}
}
.rep_ace:extend(.replace all) {}
.attributes {
[data="test"] {
extend: attributes;
}
.attribute-test {
&:extend([data="test"] all);
}
[data] {
extend: attributes2;
}
.attribute-test2 {
&:extend([data] all); //you could argue it should match [data="test"]... not for now though...
}
@attr-data: "test3";
[data=@{attr-data}] {
extend: attributes2;
}
.attribute-test {
&:extend([data="test3"] all);
}
}
.header {
.header-nav {
background: red;
&:before {
background: blue;
}
}
}
.footer {
.footer-nav {
&:extend( .header .header-nav all );
}
}
~~~~~~~~~~

View file

@ -0,0 +1,82 @@
.error {
border: 1px #f00;
background: #fdd;
}
.error.intrusion {
font-size: 1.3em;
font-weight: bold;
}
.intrusion .error {
display: none;
}
.badError {
&:extend(.error all);
border-width: 3px;
}
.foo .bar, .foo .baz {
display: none;
}
.ext1 .ext2 {
&:extend(.foo all);
}
.ext3,
.ext4 {
&:extend(.foo all);
&:extend(.bar all);
}
div.ext5,
.ext6 > .ext5 {
width: 100px;
}
.ext7 {
&:extend(.ext5 all);
}
.ext8.ext9 {
result: add-foo;
}
.ext8 .ext9,
.ext8 + .ext9,
.ext8 > .ext9 {
result: bar-matched;
}
.ext8.nomatch {
result: none;
}
.ext8 {
.ext9 {
result: match-nested-bar;
}
}
.ext8 {
&.ext9 {
result: match-nested-foo;
}
}
.fuu:extend(.ext8.ext9 all) {}
.buu:extend(.ext8 .ext9 all) {}
.zap:extend(.ext8 + .ext9 all) {}
.zoo:extend(.ext8 > .ext9 all) {}
.aa {
color: black;
.dd {
background: red;
}
}
.bb {
background: red;
.bb {
color: black;
}
}
.cc:extend(.aa,.bb) {}
.ee:extend(.dd all,.bb) {}
.ff:extend(.dd,.bb all) {}
~~~~~~~~~~

View file

@ -0,0 +1,134 @@
// simple array/list:
.multiunit {
@v: abc "abc" 1 1px 1% #123;
length: length(@v);
extract: extract(@v, 1) extract(@v, 2) extract(@v, 3) extract(@v, 4) extract(@v, 5) extract(@v, 6);
}
.incorrect-index {
@v1: a b c;
@v2: a, b, c;
v1: extract(@v1, 5);
v2: extract(@v2, -2);
}
.scalar {
@var: variable;
var-value: extract(@var, 1);
var-length: length(@var);
ill-index: extract(@var, 2);
name-value: extract(name, 1);
string-value: extract("string", 1);
number-value: extract(12345678, 1);
color-value: extract(blue, 1);
rgba-value: extract(rgba(80, 160, 240, 0.67), 1);
empty-value: extract(~'', 1);
name-length: length(name);
string-length: length("string");
number-length: length(12345678);
color-length: length(blue);
rgba-length: length(rgba(80, 160, 240, 0.67));
empty-length: length(~'');
}
.mixin-arguments {
.mixin-args(a b c d);
.mixin-args(a, b, c, d);
.mixin-args(1; 2; 3; 4);
}
.mixin-args(@value) {
&-1 {
length: length(@value);
extract: extract(@value, 3) ~"|" extract(@value, 2) ~"|" extract(@value, 1);
}
}
.mixin-args(...) {
&-2 {
length: length(@arguments);
extract: extract(@arguments, 3) ~"|" extract(@arguments, 2) ~"|" extract(@arguments, 1);
}
}
.mixin-args(@values...) {
&-3 {
length: length(@values);
extract: extract(@values, 3) ~"|" extract(@values, 2) ~"|" extract(@values, 1);
}
}
.mixin-args(@head, @tail...) {
&-4 {
length: length(@tail);
extract: extract(@tail, 2) ~"|" extract(@tail, 1);
}
}
// "multidimensional" array/list
.md-space-comma {
@v: a b c, 1 2 3, "x" "y" "z";
length-1: length(@v);
extract-1: extract(@v, 2);
length-2: length(extract(@v, 2));
extract-2: extract(extract(@v, 2), 2);
&-as-args {.mixin-args(a b c, 1 2 3, "x" "y" "z")}
}
.md-cat-space-comma {
@a: a b c;
@b: 1 2 3;
@c: "x" "y" "z";
@v: @a, @b, @c;
length-1: length(@v);
extract-1: extract(@v, 2);
length-2: length(extract(@v, 2));
extract-2: extract(extract(@v, 2), 2);
&-as-args {.mixin-args(@a, @b, @c)}
}
.md-cat-comma-space {
@a: a, b, c;
@b: 1, 2, 3;
@c: "x", "y", "z";
@v: @a @b @c;
length-1: length(@v);
extract-1: extract(@v, 2);
length-2: length(extract(@v, 2));
extract-2: extract(extract(@v, 2), 2);
&-as-args {.mixin-args(@a @b @c)}
}
.md-3D {
@a: a b c d, 1 2 3 4;
@b: 5 6 7 8, e f g h;
.3D(@a, @b);
.3D(...) {
@v1: @arguments;
length-1: length(@v1);
extract-1: extract(@v1, 1);
@v2: extract(@v1, 2);
length-2: length(@v2);
extract-2: extract(@v2, 1);
@v3: extract(@v2, 1);
length-3: length(@v3);
extract-3: extract(@v3, 3);
@v4: extract(@v3, 4);
length-4: length(@v4);
extract-4: extract(@v4, 1);
}
}
~~~~~~~~~~

View file

@ -0,0 +1,169 @@
#functions {
@var: 10;
@colors: #000, #fff;
color: _color("evil red"); // #660000
width: increment(15);
height: undefined("self");
border-width: add(2, 3);
variable: increment(@var);
background: linear-gradient(@colors);
}
#built-in {
@r: 32;
escaped: e("-Some::weird(#thing, y)");
lighten: lighten(#ff0000, 40%);
darken: darken(#ff0000, 40%);
saturate: saturate(#29332f, 20%);
desaturate: desaturate(#203c31, 20%);
greyscale: greyscale(#203c31);
hsl-clamp: hsl(380, 150%, 150%);
spin-p: spin(hsl(340, 50%, 50%), 40);
spin-n: spin(hsl(30, 50%, 50%), -40);
luma-white: luma(#fff);
luma-black: luma(#000);
luma-black-alpha: luma(rgba(0,0,0,0.5));
luma-red: luma(#ff0000);
luma-green: luma(#00ff00);
luma-blue: luma(#0000ff);
luma-yellow: luma(#ffff00);
luma-cyan: luma(#00ffff);
luma-white-alpha: luma(rgba(255,255,255,0.5));
contrast-filter: contrast(30%);
saturate-filter: saturate(5%);
contrast-white: contrast(#fff);
contrast-black: contrast(#000);
contrast-red: contrast(#ff0000);
contrast-green: contrast(#00ff00);
contrast-blue: contrast(#0000ff);
contrast-yellow: contrast(#ffff00);
contrast-cyan: contrast(#00ffff);
contrast-light: contrast(#fff, #111111, #eeeeee);
contrast-dark: contrast(#000, #111111, #eeeeee);
contrast-wrongorder: contrast(#fff, #eeeeee, #111111, 0.5);
contrast-light-thresh: contrast(#fff, #111111, #eeeeee, 0.5);
contrast-dark-thresh: contrast(#000, #111111, #eeeeee, 0.5);
contrast-high-thresh: contrast(#555, #111111, #eeeeee, 0.6);
contrast-low-thresh: contrast(#555, #111111, #eeeeee, 0.1);
contrast-light-thresh-per: contrast(#fff, #111111, #eeeeee, 50%);
contrast-dark-thresh-per: contrast(#000, #111111, #eeeeee, 50%);
contrast-high-thresh-per: contrast(#555, #111111, #eeeeee, 60%);
contrast-low-thresh-per: contrast(#555, #111111, #eeeeee, 10%);
replace: replace("Hello, Mars.", "Mars\.", "World!");
replace-captured: replace("This is a string.", "(string)\.$", "new $1.");
replace-with-flags: replace("One + one = 4", "one", "2", "gi");
replace-single-quoted: replace('foo-1', "1", "2");
replace-escaped-string: replace(~"bar-1", "1", "2");
replace-keyword: replace(baz-1, "1", "2");
format: %("rgb(%d, %d, %d)", @r, 128, 64);
format-string: %("hello %s", "world");
format-multiple: %("hello %s %d", "earth", 2);
format-url-encode: %("red is %A", #ff0000);
format-single-quoted: %('hello %s', "single world");
format-escaped-string: %(~"hello %s", "escaped world");
eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64));
unitless: unit(12px);
unit: unit((13px + 1px), em);
unitpercentage: unit(100, %);
get-unit: get-unit(10px);
get-unit-empty: get-unit(10);
hue: hue(hsl(98, 12%, 95%));
saturation: saturation(hsl(98, 12%, 95%));
lightness: lightness(hsl(98, 12%, 95%));
hsvhue: hsvhue(hsv(98, 12%, 95%));
hsvsaturation: hsvsaturation(hsv(98, 12%, 95%));
hsvvalue: hsvvalue(hsv(98, 12%, 95%));
red: red(#f00);
green: green(#0f0);
blue: blue(#00f);
rounded: round((@r/3));
rounded-two: round((@r/3), 2);
roundedpx: round((10px / 3));
roundedpx-three: round((10px / 3), 3);
rounded-percentage: round(10.2%);
ceil: ceil(10.1px);
floor: floor(12.9px);
sqrt: sqrt(25px);
pi: pi();
mod: mod(13m, 11cm); // could take into account units, doesn't at the moment
abs: abs(-4%);
tan: tan(42deg);
sin: sin(10deg);
cos: cos(12);
atan: atan(tan(0.1rad));
atan: convert(acos(cos(34deg)), deg);
atan: convert(acos(cos(50grad)), deg);
pow: pow(8px, 2);
pow: pow(4, 3);
pow: pow(3, 3em);
min: min(0);
min: min(6, 5);
min: min(1pt, 3pt);
min: min(1cm, 3mm);
max: max(1, 3);
max: max(3em, 1em, 2em, 5em);
percentage: percentage((10px / 50));
color: color("#ff0011");
tint: tint(#777777, 13);
tint-full: tint(#777777, 100);
tint-percent: tint(#777777, 13%);
tint-negative: tint(#777777, -13%);
shade: shade(#777777, 13);
shade-full: shade(#777777, 100);
shade-percent: shade(#777777, 13%);
shade-negative: shade(#777777, -13%);
fade-out: fadeOut(red, 5%); // support fadeOut and fadeout
fade-in: fadein(fadeout(red, 10%), 5%);
hsv: hsv(5, 50%, 30%);
hsva: hsva(3, 50%, 30%, 0.2);
mix: mix(#ff0000, #ffff00, 80);
mix-0: mix(#ff0000, #ffff00, 0);
mix-100: mix(#ff0000, #ffff00, 100);
mix-weightless: mix(#ff0000, #ffff00);
mixt: mix(#ff0000, transparent);
.is-a {
color: iscolor(#ddd);
color1: iscolor(red);
color2: iscolor(rgb(0, 0, 0));
color3: iscolor(transparent);
keyword: iskeyword(hello);
number: isnumber(32);
string: isstring("hello");
pixel: ispixel(32px);
percent: ispercentage(32%);
em: isem(32em);
cat: isunit(32cat, cat);
}
}
#alpha {
alpha: darken(hsla(25, 50%, 50%, 0.6), 10%);
alpha2: alpha(rgba(3, 4, 5, 0.5));
alpha3: alpha(transparent);
}
#blendmodes {
multiply: multiply(#f60000, #f60000);
screen: screen(#f60000, #0000f6);
overlay: overlay(#f60000, #0000f6);
softlight: softlight(#f60000, #ffffff);
hardlight: hardlight(#f60000, #0000f6);
difference: difference(#f60000, #0000f6);
exclusion: exclusion(#f60000, #0000f6);
average: average(#f60000, #0000f6);
negation: negation(#f60000, #313131);
}
#extract-and-length {
@anon: A B C 1 2 3;
extract: extract(@anon, 6) extract(@anon, 5) extract(@anon, 4) extract(@anon, 3) extract(@anon, 2) extract(@anon, 1);
length: length(@anon);
}
~~~~~~~~~~

View file

@ -0,0 +1,16 @@
@fat: 0;
@cloudhead: "#000000";
.nav {
filter: progid:DXImageTransform.Microsoft.Alpha(opacity = 20);
filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@fat);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#333333", endColorstr=@cloudhead, GradientType=@fat);
}
.evalTest(@arg) {
filter: progid:DXImageTransform.Microsoft.Alpha(opacity=@arg);
}
.evalTest1 {
.evalTest(30);
.evalTest(5);
}
~~~~~~~~~~

View file

@ -0,0 +1,30 @@
.eval {
js: `42`;
js: `1 + 1`;
js: `"hello world"`;
js: `[1, 2, 3]`;
title: `typeof process.title`;
ternary: `(1 + 1 == 2 ? true : false)`;
multiline: `(function(){var x = 1 + 1;
return x})()`;
}
.scope {
@foo: 42;
var: `parseInt(this.foo.toJS())`;
escaped: ~`2 + 5 + 'px'`;
}
.vars {
@var: `4 + 4`;
width: @var;
}
.escape-interpol {
@world: "world";
width: ~`"hello" + " " + @{world}`;
}
.arrays {
@ary: 1, 2, 3;
@ary2: 1 2 3;
ary: `@{ary}.join(', ')`;
ary1: `@{ary2}.join(', ')`;
}
~~~~~~~~~~

View file

@ -0,0 +1,7 @@
@var: @a;
@a: 100%;
.lazy-eval {
width: @var;
}
~~~~~~~~~~

View file

@ -0,0 +1,235 @@
// For now, variables can't be declared inside @media blocks.
@var: 42;
@media print {
.class {
color: blue;
.sub {
width: @var;
}
}
.top, header > h1 {
color: (#222 * 2);
}
}
@media screen {
@base: 8;
body { max-width: (@base * 60); }
}
@ratio_large: 16;
@ratio_small: 9;
@media all and (device-aspect-ratio: @ratio_large / @ratio_small) {
body { max-width: 800px; }
}
@media all and (orientation:portrait) {
aside { float: none; }
}
@media handheld and (min-width: @var), screen and (min-width: 20em) {
body {
max-width: 480px;
}
}
body {
@media print {
padding: 20px;
header {
background-color: red;
}
@media (orientation:landscape) {
margin-left: 20px;
}
}
}
@media screen {
.sidebar {
width: 300px;
@media (orientation: landscape) {
width: 500px;
}
}
}
@media a {
.first {
@media b {
.second {
.third {
width: 300px;
@media c {
width: 500px;
}
}
.fourth {
width: 3;
}
}
}
}
}
body {
@media a, b and c {
width: 95%;
@media x, y {
width: 100%;
}
}
}
.mediaMixin(@fallback: 200px) {
background: black;
@media handheld {
background: white;
@media (max-width: @fallback) {
background: red;
}
}
}
.a {
.mediaMixin(100px);
}
.b {
.mediaMixin();
}
@smartphone: ~"only screen and (max-width: 200px)";
@media @smartphone {
body {
width: 480px;
}
}
@media print {
@page :left {
margin: 0.5cm;
}
@page :right {
margin: 0.5cm;
}
@page Test:first {
margin: 1cm;
}
@page :first {
size: 8.5in 11in;
@top-left {
margin: 1cm;
}
@top-left-corner {
margin: 1cm;
}
@top-center {
margin: 1cm;
}
@top-right {
margin: 1cm;
}
@top-right-corner {
margin: 1cm;
}
@bottom-left {
margin: 1cm;
}
@bottom-left-corner {
margin: 1cm;
}
@bottom-center {
margin: 1cm;
}
@bottom-right {
margin: 1cm;
}
@bottom-right-corner {
margin: 1cm;
}
@left-top {
margin: 1cm;
}
@left-middle {
margin: 1cm;
}
@left-bottom {
margin: 1cm;
}
@right-top {
margin: 1cm;
}
@right-middle {
content: "Page " counter(page);
}
@right-bottom {
margin: 1cm;
}
}
}
@media (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-resolution: 2dppx), (min-resolution: 128dpcm) {
.b {
background: red;
}
}
.bg() {
background: red;
@media (max-width: 500px) {
background: green;
}
}
body {
.bg();
}
@bpMedium: 1000px;
@media (max-width: @bpMedium) {
body {
.bg();
background: blue;
}
}
@media (max-width: 1200px) {
/* a comment */
@media (max-width: 900px) {
body { font-size: 11px; }
}
}
.nav-justified {
@media (min-width: 480px) {
> li {
display: table-cell;
}
}
}
.menu
{
@media (min-width: 768px) {
.nav-justified();
}
}
@all: ~"all";
@tv: ~"tv";
@media @all and @tv {
.all-and-tv-variables {
var: all-and-tv;
}
}
~~~~~~~~~~

View file

@ -0,0 +1,60 @@
.first-transform() {
transform+: rotate(90deg), skew(30deg);
}
.second-transform() {
transform+: scale(2,4);
}
.third-transform() {
transform: scaleX(45deg);
}
.fourth-transform() {
transform+: scaleX(45deg);
}
.fifth-transform() {
transform+: scale(2,4) !important;
}
.first-background() {
background+: url(data://img1.png);
}
.second-background() {
background+: url(data://img2.png);
}
.test1 {
// Can merge values
.first-transform();
.second-transform();
}
.test2 {
// Wont merge values without +: merge directive, for backwards compatibility with css
.first-transform();
.third-transform();
}
.test3 {
// Wont merge values from two sources with different properties
.fourth-transform();
.first-background();
}
.test4 {
// Wont merge values from sources that merked as !important, for backwards compatibility with css
.first-transform();
.fifth-transform();
}
.test5 {
// Wont merge values from mixins that merked as !important, for backwards compatibility with css
.first-transform();
.second-transform() !important;
}
.test6 {
// Ignores !merge if no peers found
.second-transform();
}
.test-interleaved {
transform+: t1;
background+: b1;
transform+: t2;
background+: b2, b3;
transform+: t3;
}
~~~~~~~~~~

View file

@ -0,0 +1,216 @@
.mixin (@a: 1px, @b: 50%) {
width: (@a * 5);
height: (@b - 1%);
}
.mixina (@style, @width, @color: black) {
border: @width @style @color;
}
.mixiny
(@a: 0, @b: 0) {
margin: @a;
padding: @b;
}
.hidden() {
color: transparent; // asd
}
#hidden {
.hidden;
}
#hidden1 {
.hidden();
}
.two-args {
color: blue;
.mixin(2px, 100%);
.mixina(dotted, 2px);
}
.one-arg {
.mixin(3px);
}
.no-parens {
.mixin;
}
.no-args {
.mixin();
}
.var-args {
@var: 9;
.mixin(@var, (@var * 2));
}
.multi-mix {
.mixin(2px, 30%);
.mixiny(4, 5);
}
.maxa(@arg1: 10, @arg2: #f00) {
padding: (@arg1 * 2px);
color: @arg2;
}
body {
.maxa(15);
}
@glob: 5;
.global-mixin(@a:2) {
width: (@glob + @a);
}
.scope-mix {
.global-mixin(3);
}
.nested-ruleset (@width: 200px) {
width: @width;
.column { margin: @width; }
}
.content {
.nested-ruleset(600px);
}
//
.same-var-name2(@radius) {
radius: @radius;
}
.same-var-name(@radius) {
.same-var-name2(@radius);
}
#same-var-name {
.same-var-name(5px);
}
//
.var-inside () {
@var: 10px;
width: @var;
}
#var-inside { .var-inside; }
.mixin-arguments (@width: 0px, ...) {
border: @arguments;
width: @width;
}
.arguments {
.mixin-arguments(1px, solid, black);
}
.arguments2 {
.mixin-arguments();
}
.arguments3 {
.mixin-arguments;
}
.mixin-arguments2 (@width, @rest...) {
border: @arguments;
rest: @rest;
width: @width;
}
.arguments4 {
.mixin-arguments2(0, 1, 2, 3, 4);
}
// Edge cases
.edge-case {
.mixin-arguments("{");
}
// Division vs. Literal Slash
.border-radius(@r: 2px/5px) {
border-radius: @r;
}
.slash-vs-math {
.border-radius();
.border-radius(5px/10px);
.border-radius((3px * 2));
}
// semi-colon vs comma for delimiting
.mixin-takes-one(@a) {
one: @a;
}
.mixin-takes-two(@a; @b) {
one: @a;
two: @b;
}
.comma-vs-semi-colon {
.mixin-takes-two(@a : a; @b : b, c);
.mixin-takes-two(@a : d, e; @b : f);
.mixin-takes-one(@a: g);
.mixin-takes-one(@a : h;);
.mixin-takes-one(i);
.mixin-takes-one(j;);
.mixin-takes-two(k, l);
.mixin-takes-one(m, n;);
.mixin-takes-two(o, p; q);
.mixin-takes-two(r, s; t;);
}
.mixin-conflict(@a:defA, @b:defB, @c:defC) {
three: @a, @b, @c;
}
.mixin-conflict(@a:defA, @b:defB, @c:defC, @d:defD) {
four: @a, @b, @c, @d;
}
#named-conflict {
.mixin-conflict(11, 12, 13, @a:a);
.mixin-conflict(@a:a, 21, 22, 23);
}
@a: 3px;
.mixin-default-arg(@a: 1px, @b: @a, @c: @b) {
defaults: 1px 1px 1px;
defaults: 2px 2px 2px;
}
.test-mixin-default-arg {
.mixin-default-arg();
.mixin-default-arg(2px);
}
.mixin-comma-default1(@color; @padding; @margin: 2, 2, 2, 2) {
margin: @margin;
}
.selector {
.mixin-comma-default1(#33acfe; 4);
}
.mixin-comma-default2(@margin: 2, 2, 2, 2;) {
margin: @margin;
}
.selector2 {
.mixin-comma-default2();
}
.mixin-comma-default3(@margin: 2, 2, 2, 2) {
margin: @margin;
}
.selector3 {
.mixin-comma-default3(4,2,2,2);
}
.test-calling-one-arg-mixin(@a) {
}
.test-calling-one-arg-mixin(@a, @b, @rest...) {
}
div {
.test-calling-one-arg-mixin(1);
}
~~~~~~~~~~

View file

@ -0,0 +1,27 @@
.scope {
@var: 99px;
.mixin () {
width: @var;
}
}
.class {
.scope > .mixin;
}
.overwrite {
@var: 0px;
.scope > .mixin;
}
.nested {
@var: 5px;
.mixin () {
width: @var;
}
.class {
@var: 10px;
.mixin;
}
}
~~~~~~~~~~

View file

@ -0,0 +1,196 @@
// basics:
guard-default-basic-1 {
.m(1) {case: 1}
.m(@x) when (default()) {default: @x}
&-1 {.m(1)}
&-2 {.m(2)}
}
guard-default-basic-2 {
.m(1) {case: 1}
.m(2) {case: 2}
.m(3) {case: 3}
.m(@x) when (default()) {default: @x}
&-0 {.m(0)}
&-2 {.m(2)}
}
guard-default-basic-3 {
.m(@x) when (@x = 1) {case: 1}
.m(2) {case: 2}
.m(@x) when (@x = 3) {case: 3}
.m(@x) when (default()) {default: @x}
&-0 {.m(0)}
&-2 {.m(2)}
&-3 {.m(3)}
}
guard-default-definition-order {
.m(@x) when (default()) {default: @x}
.m(@x) when (@x = 1) {case: 1}
.m(2) {case: 2}
.m(@x) when (@x = 3) {case: 3}
&-0 {.m(0)}
&-2 {.m(2)}
&-2 {.m(3)}
}
// out of guard:
guard-default-out-of-guard {
.m(1) {case-1: 1}
.m(@x: default()) when (default()) {default: @x}
&-0 {
case-0: default();
.m(1);
.m(2);
case-2: default();
}
&-1 {.m(default())}
&-2 {.m()}
}
// expressions:
guard-default-expr-not {
.m(1) {case: 1}
.m(@x) when not(default()) {default: @x}
&-1 {.m(1)}
&-2 {.m(2)}
}
guard-default-expr-eq {
.m(@x) when (@x = true) {case: @x}
.m(@x) when (@x = false) {case: @x}
.m(@x) when (@x = default()) {default: @x}
&-true {.m(true)}
&-false {.m(false)}
}
guard-default-expr-or {
.m(1) {case: 1}
.m(2) {case: 2}
.m(@x) when (default()), (@x = 2) {default: @x}
&-1 {.m(1)}
&-2 {.m(2)}
&-3 {.m(3)}
}
guard-default-expr-and {
.m(1) {case: 1}
.m(2) {case: 2}
.m(@x) when (default()) and (@x = 3) {default: @x}
&-1 {.m(1)}
&-2 {.m(2)}
&-3 {.m(3)}
&-4 {.m(4)}
}
guard-default-expr-always {
.m(1) {case: 1}
.m(@x) when (default()), not(default()) {default: @x} // always match
&-1 {.m(1)}
&-2 {.m(2)}
}
guard-default-expr-never {
.m(1) {case: 1}
.m(@x) when (default()) and not(default()) {default: @x} // never match
&-1 {.m(1)}
&-2 {.m(2)}
}
// not conflicting multiple default() uses:
guard-default-multi-1 {
.m(0) {case: 0}
.m(@x) when (default()) {default-1: @x}
.m(2) when (default()) {default-2: @x}
&-0 {.m(0)}
&-1 {.m(1)}
}
guard-default-multi-2 {
.m(1, @x) when (default()) {default-1: @x}
.m(2, @x) when (default()) {default-2: @x}
.m(@x, yes) when (default()) {default-3: @x}
&-1 {.m(1, no)}
&-2 {.m(2, no)}
&-3 {.m(3, yes)}
}
guard-default-multi-3 {
.m(red) {case-1: darkred}
.m(blue) {case-2: darkblue}
.m(@x) when (iscolor(@x)) and (default()) {default-color: @x}
.m('foo') {case-1: I am 'foo'}
.m('bar') {case-2: I am 'bar'}
.m(@x) when (isstring(@x)) and (default()) {default-string: I am @x}
&-blue {.m(blue)}
&-green {.m(green)}
&-foo {.m('foo')}
&-baz {.m('baz')}
}
guard-default-multi-4 {
.m(@x) when (default()), not(default()) {always: @x}
.m(@x) when (default()) and not(default()) {never: @x}
.m(2) {case: 2}
.m(1);
.m(2);
}
guard-default-not-ambiguos-2 {
.m(@x) {case: 1}
.m(@x) when (default()) {default: @x}
.m(@x) when not(default()) {not-default: @x}
.m(2);
}
guard-default-not-ambiguos-3 {
.m(@x) {case: 1}
.m(@x) when not(default()) {not-default-1: @x}
.m(@x) when not(default()) {not-default-2: @x}
.m(2);
}
// default & scope
guard-default-scopes {
.s1() {.m(@v) {1: no condition}}
.s2() {.m(@v) when (@v) {2: when true}}
.s3() {.m(@v) when (default()) {3: when default}}
&-3 {
.s2();
.s3();
.m(false);
}
&-1 {
.s1();
.s3();
.m(false);
}
}
~~~~~~~~~~

View file

@ -0,0 +1,154 @@
// Stacking, functions..
.light (@a) when (lightness(@a) > 50%) {
color: white;
}
.light (@a) when (lightness(@a) < 50%) {
color: black;
}
.light (@a) {
margin: 1px;
}
.light1 { .light(#ddd) }
.light2 { .light(#444) }
// Arguments against each other
.max (@a, @b) when (@a > @b) {
width: @a;
}
.max (@a, @b) when (@a < @b) {
width: @b;
}
.max1 { .max(3, 6) }
.max2 { .max(8, 1) }
// Globals inside guards
@g: auto;
.glob (@a) when (@a = @g) {
margin: @a @g;
}
.glob1 { .glob(auto) }
// Other operators
.ops (@a) when (@a >= 0) {
height: gt-or-eq;
}
.ops (@a) when (@a =< 0) {
height: lt-or-eq;
}
.ops (@a) when (@a <= 0) {
height: lt-or-eq-alias;
}
.ops (@a) when not(@a = 0) {
height: not-eq;
}
.ops1 { .ops(0) }
.ops2 { .ops(1) }
.ops3 { .ops(-1) }
// Scope and default values
@a: auto;
.default (@a: inherit) when (@a = inherit) {
content: default;
}
.default1 { .default }
// true & false keywords
.test (@a) when (@a) {
content: "true.";
}
.test (@a) when not (@a) {
content: "false.";
}
.test1 { .test(true) }
.test2 { .test(false) }
.test3 { .test(1) }
.test4 { .test(boo) }
.test5 { .test("true") }
// Boolean expressions
.bool () when (true) and (false) { content: true and false } // FALSE
.bool () when (true) and (true) { content: true and true } // TRUE
.bool () when (true) { content: true } // TRUE
.bool () when (false) and (false) { content: true } // FALSE
.bool () when (false), (true) { content: false, true } // TRUE
.bool () when (false) and (true) and (true), (true) { content: false and true and true, true } // TRUE
.bool () when (true) and (true) and (false), (false) { content: true and true and false, false } // FALSE
.bool () when (false), (true) and (true) { content: false, true and true } // TRUE
.bool () when (false), (false), (true) { content: false, false, true } // TRUE
.bool () when (false), (false) and (true), (false) { content: false, false and true, false } // FALSE
.bool () when (false), (true) and (true) and (true), (false) { content: false, true and true and true, false } // TRUE
.bool () when not (false) { content: not false }
.bool () when not (true) and not (false) { content: not true and not false }
.bool () when not (true) and not (true) { content: not true and not true }
.bool () when not (false) and (false), not (false) { content: not false and false, not false }
.bool1 { .bool }
.equality-unit-test(@num) when (@num = 1%) {
test: fail;
}
.equality-unit-test(@num) when (@num = 2) {
test: pass;
}
.equality-units {
.equality-unit-test(1px);
.equality-unit-test(2px);
}
.colorguard(@col) when (@col = red) { content: is @col; }
.colorguard(@col) when not (blue = @col) { content: is not blue its @col; }
.colorguard(@col) {}
.colorguardtest {
.colorguard(red);
.colorguard(blue);
.colorguard(purple);
}
.stringguard(@str) when (@str = "theme1") { content: is theme1; }
.stringguard(@str) when not ("theme2" = @str) { content: is not theme2; }
.stringguard(@str) when (~"theme1" = @str) { content: is theme1 no quotes; }
.stringguard(@str) {}
.stringguardtest {
.stringguard("theme1");
.stringguard("theme2");
.stringguard(theme1);
}
.mixin(...) {
catch:all;
}
.mixin(@var) when (@var=4) {
declare: 4;
}
.mixin(@var) when (@var=4px) {
declare: 4px;
}
#tryNumberPx {
.mixin(4px);
}
.lock-mixin(@a) {
.inner-locked-mixin(@x: @a) when (@a = 1) {
a: @a;
x: @x;
}
}
.call-lock-mixin {
.lock-mixin(1);
.call-inner-lock-mixin {
.inner-locked-mixin();
}
}
~~~~~~~~~~

View file

@ -0,0 +1,26 @@
.submixin(@a) {
border-width: @a;
}
.mixin (9) {
border: 9 !important;
}
.mixin (@a: 0) {
border: @a;
boxer: @a;
.inner {
test: @a;
}
// comment
.submixin(@a);
}
.class {
.mixin(1);
.mixin(2) !important;
.mixin(3);
.mixin(4) !important;
.mixin(5);
.mixin !important;
.mixin(9);
}
~~~~~~~~~~

View file

@ -0,0 +1,70 @@
@a1: foo;
@a2: ~".foo";
@a4: ~"#foo";
.@{a1} {
a: 1;
}
@{a2} {
a: 2;
}
#@{a1} {
a: 3;
}
@{a4} {
a: 4;
}
mi-test-a {
.foo;
#foo;
}
.b .bb {
&.@{a1}-xxx .yyy-@{a1}@{a4} {
& @{a2}.bbb {
b: 1;
}
}
}
mi-test-b {
.b.bb.foo-xxx.yyy-foo#foo.foo.bbb;
}
@c1: @a1;
@c2: bar;
@c3: baz;
#@{c1}-foo {
> .@{c2} {
.@{c3} {
c: c;
}
}
}
mi-test-c {
&-1 {#foo-foo;}
&-2 {#foo-foo > .bar;}
&-3 {#foo-foo > .bar.baz;}
}
.Person(@name, @gender_) {
.@{name} {
@gender: @gender_;
.sayGender() {
gender: @gender;
}
}
}
mi-test-d {
.Person(person, "Male");
.person.sayGender();
}
~~~~~~~~~~

View file

@ -0,0 +1,37 @@
.mixin (@a: 1px, @b: 50%) {
width: (@a * 5);
height: (@b - 1%);
args: @arguments;
}
.mixin (@a: 1px, @b: 50%) when (@b > 75%){
text-align: center;
}
.named-arg {
color: blue;
.mixin(@b: 100%);
}
.class {
@var: 20%;
.mixin(@b: @var);
}
.all-args-wrong-args {
.mixin(@b: 10%, @a: 2px);
}
.mixin2 (@a: 1px, @b: 50%, @c: 50) {
width: (@a * 5);
height: (@b - 1%);
color: (#000000 + @c);
}
.named-args2 {
.mixin2(3px, @c: 100);
}
.named-args3 {
.mixin2(@b: 30%, @c: #123456);
}
~~~~~~~~~~

View file

@ -0,0 +1,23 @@
.mix-inner (@var) {
border-width: @var;
}
.mix (@a: 10) {
.inner {
height: (@a * 10);
.innest {
width: @a;
.mix-inner((@a * 2));
}
}
}
.class {
.mix(30);
}
.class2 {
.mix(60);
}
~~~~~~~~~~

View file

@ -0,0 +1,103 @@
.mixin (...) {
variadic: true;
}
.mixin (@a...) {
named-variadic: true;
}
.mixin () {
zero: 0;
}
.mixin (@a: 1px) {
one: 1;
}
.mixin (@a) {
one-req: 1;
}
.mixin (@a: 1px, @b: 2px) {
two: 2;
}
.mixin (@a, @b, @c) {
three-req: 3;
}
.mixin (@a: 1px, @b: 2px, @c: 3px) {
three: 3;
}
.zero {
.mixin();
}
.one {
.mixin(1);
}
.two {
.mixin(1, 2);
}
.three {
.mixin(1, 2, 3);
}
//
.mixout ('left') {
left: 1;
}
.mixout ('right') {
right: 1;
}
.left {
.mixout('left');
}
.right {
.mixout('right');
}
//
.border (@side, @width) {
color: black;
.border-side(@side, @width);
}
.border-side (left, @w) {
border-left: @w;
}
.border-side (right, @w) {
border-right: @w;
}
.border-right {
.border(right, 4px);
}
.border-left {
.border(left, 4px);
}
//
.border-radius (@r) {
both: (@r * 10);
}
.border-radius (@r, left) {
left: @r;
}
.border-radius (@r, right) {
right: @r;
}
.only-right {
.border-radius(33, right);
}
.only-left {
.border-radius(33, left);
}
.left-right {
.border-radius(33);
}
~~~~~~~~~~

View file

@ -0,0 +1,142 @@
.mixin { border: 1px solid black; }
.mixout { border-color: orange; }
.borders { border-style: dashed; }
#namespace {
.borders {
border-style: dotted;
}
.biohazard {
content: "death";
.man {
color: transparent;
}
}
}
#theme {
> .mixin {
background-color: grey;
}
}
#container {
color: black;
.mixin;
.mixout;
#theme > .mixin;
}
#header {
.milk {
color: white;
.mixin;
#theme > .mixin;
}
#cookie {
.chips {
#namespace .borders;
.calories {
#container;
}
}
.borders;
}
}
.secure-zone { #namespace .biohazard .man; }
.direct {
#namespace > .borders;
}
.bo, .bar {
width: 100%;
}
.bo {
border: 1px;
}
.ar.bo.ca {
color: black;
}
.jo.ki {
background: none;
}
.amp {
&.support {
color: orange;
.higher {
top: 0px;
}
&.deeper {
height: auto;
}
}
}
.extended {
.bo;
.jo.ki;
.amp.support;
.amp.support.higher;
.amp.support.deeper;
}
.do .re .mi .fa {
.sol .la {
.si {
color: cyan;
}
}
}
.mutli-selector-parents {
.do.re.mi.fa.sol.la.si;
}
.foo .bar {
.bar;
}
.has_parents() {
& .underParents {
color: red;
}
}
.has_parents();
.parent {
.has_parents();
}
.margin_between(@above, @below) {
* + & { margin-top: @above; }
legend + & { margin-top: 0; }
& + * { margin-top: @below; }
}
h1 { .margin_between(25px, 10px); }
h2 { .margin_between(20px, 8px); }
h3 { .margin_between(15px, 5px); }
.mixin_def(@url, @position){
background-image: @url;
background-position: @position;
}
.error{
@s: "/";
.mixin_def( "@{s}a.png", center center);
}
.recursion() {
color: black;
}
.test-rec {
.recursion {
.recursion();
}
}
.paddingFloat(@padding) { padding-left: @padding; }
.button {
.paddingFloat(((10px + 12) * 2));
&.large { .paddingFloat(((10em * 2) * 2)); }
}
.clearfix() {
// ...
}
.clearfix {
.clearfix();
}
.foo {
.clearfix();
}
~~~~~~~~~~

View file

@ -0,0 +1,3 @@
.mixin() {
}
~~~~~~~~~~

View file

@ -0,0 +1,63 @@
#operations {
color: (#110000 + #000011 + #001100); // #111111
height: (10px / 2px + 6px - 1px * 2); // 9px
width: (2 * 4 - 5em); // 3em
.spacing {
height: (10px / 2px+6px-1px*2);
width: (2 * 4-5em);
}
substraction: (20 - 10 - 5 - 5); // 0
division: (20 / 5 / 4); // 1
}
@x: 4;
@y: 12em;
.with-variables {
height: (@x + @y); // 16em
width: (12 + @y); // 24em
size: (5cm - @x); // 1cm
}
.with-functions {
color: (rgb(200, 200, 200) / 2);
color: (2 * hsl(0, 50%, 50%));
color: (rgb(10, 10, 10) + hsl(0, 50%, 50%));
}
@z: -2;
.negative {
height: (2px + @z); // 0px
width: (2px - @z); // 4px
}
.shorthands {
padding: -1px 2px 0 -4px; //
}
.rem-dimensions {
font-size: (20rem / 5 + 1.5rem); // 5.5rem
}
.colors {
color: #123; // #112233
border-color: (#234 + #111111); // #334455
background-color: (#222222 - #fff); // #000000
.other {
color: (2 * #111); // #222222
border-color: (#333333 / 3 + #111); // #222222
}
}
.negations {
@var: 4px;
variable: (-@var); // 4
variable1: (-@var + @var); // 0
variable2: (@var + -@var); // 0
variable3: (@var - -@var); // 8
variable4: (-@var - -@var); // 0
paren: (-(@var)); // -4px
paren2: (-(2 + 2) * -@var); // 16
}
~~~~~~~~~~

View file

@ -0,0 +1,46 @@
.parens {
@var: 1px;
border: (@var * 2) solid black;
margin: (@var * 1) (@var + 2) (4 * 4) 3;
width: (6 * 6);
padding: 2px (6 * 6px);
}
.more-parens {
@var: (2 * 2);
padding: (2 * @var) 4 4 (@var * 1px);
width-all: ((@var * @var) * 6);
width-first: ((@var * @var)) * 6;
width-keep: (@var * @var) * 6;
height-keep: (7 * 7) + (8 * 8);
height-all: ((7 * 7) + (8 * 8));
height-parts: ((7 * 7)) + ((8 * 8));
margin-keep: (4 * (5 + 5) / 2) - (@var * 2);
margin-parts: ((4 * (5 + 5) / 2)) - ((@var * 2));
margin-all: ((4 * (5 + 5) / 2) + (-(@var * 2)));
border-radius-keep: 4px * (1 + 1) / @var + 3px;
border-radius-parts: ((4px * (1 + 1))) / ((@var + 3px));
border-radius-all: (4px * (1 + 1) / @var + 3px);
//margin: (6 * 6)px;
}
.negative {
@var: 1;
neg-var: -@var; // -1 ?
neg-var-paren: -(@var); // -(1) ?
}
.nested-parens {
width: 2 * (4 * (2 + (1 + 6))) - 1;
height: ((2 + 3) * (2 + 3) / (9 - 4)) + 1;
}
.mixed-units {
margin: 2px 4em 1 5pc;
padding: (2px + 4px) 1em 2px 2;
}
.test-false-negatives {
a: ~"(";
}
~~~~~~~~~~

View file

@ -0,0 +1,42 @@
test {
@prefix: ufo-;
@a: border;
@bb: top;
@c_c: left;
@d-d4: radius;
@-: -;
@{a}: 0;
@{prefix}width: 50%;
*-z-@{a} :1px dashed blue;
-www-@{a}-@{bb}: 2px;
@{d-d4}-is-not-a-@{a}:true;
@{a}-@{bb}-@{c_c}-@{d-d4} : 2em;
@{a}@{-}@{bb}@{-}red@{-}@{d-d4}-: 3pt;
.mixin(mixer);
.merge(ish, base);
}
@global: global;
.mixin(@arg) {
@local: local;
@{global}-@{local}-@{arg}-property: strong;
}
.merge(@p, @v) {
&-merge {
@prefix: pre;
@suffix: ish;
@{prefix}-property-ish+ :high;
pre-property-@{suffix} +: middle;
@{prefix}-property-@{suffix}+: low;
@{prefix}-property-@{p} + : @v;
@subterfuge: ~'+';
pre-property-ish@{subterfuge}: nice try dude;
}
}
~~~~~~~~~~

View file

@ -0,0 +1,31 @@
#first > .one {
> #second .two > #deux {
width: 50%;
#third {
&:focus {
color: black;
#fifth {
> #sixth {
.seventh #eighth {
+ #ninth {
color: purple;
}
}
}
}
}
height: 100%;
}
#fourth, #five, #six {
color: #110000;
.seven, .eight > #nine {
border: 1px solid black;
}
#ten {
color: red;
}
}
}
font-size: 2em;
}
~~~~~~~~~~

View file

@ -0,0 +1,105 @@
@x: red;
@x: blue;
@z: transparent;
@mix: none;
.mixin {
@mix: #989;
}
@mix: blue;
.tiny-scope {
color: @mix; // #989
.mixin;
}
.scope1 {
@y: orange;
@z: black;
color: @x; // blue
border-color: @z; // black
.hidden {
@x: #131313;
}
.scope2 {
@y: red;
color: @x; // blue
.scope3 {
@local: white;
color: @y; // red
border-color: @z; // black
background-color: @local; // white
}
}
}
#namespace {
.scoped_mixin() {
@local-will-be-made-global: green;
.scope {
scoped-val: @local-will-be-made-global;
}
}
}
#namespace > .scoped_mixin();
.setHeight(@h) { @height: 1024px; }
.useHeightInMixinCall(@h) { .useHeightInMixinCall { mixin-height: @h; } }
@mainHeight: 50%;
.setHeight(@mainHeight);
.heightIsSet { height: @height; }
.useHeightInMixinCall(@height);
.importRuleset() {
.imported {
exists: true;
}
}
.importRuleset();
.testImported {
.imported;
}
@parameterDefault: 'top level';
@anotherVariable: 'top level';
//mixin uses top-level variables
.mixinNoParam(@parameter: @parameterDefault) when (@parameter = 'top level') {
default: @parameter;
scope: @anotherVariable;
sub-scope-only: @subScopeOnly;
}
#allAreUsedHere {
//redefine top-level variables in different scope
@parameterDefault: 'inside';
@anotherVariable: 'inside';
@subScopeOnly: 'inside';
//use the mixin
.mixinNoParam();
}
#parentSelectorScope {
@col: white;
& {
@col: black;
}
prop: @col;
& {
@col: black;
}
}
.test-empty-mixin() {
}
#parentSelectorScopeMixins {
& {
.test-empty-mixin() {
should: never seee 1;
}
}
.test-empty-mixin();
& {
.test-empty-mixin() {
should: never seee 2;
}
}
}
~~~~~~~~~~

View file

@ -0,0 +1,157 @@
h1, h2, h3 {
a, p {
&:hover {
color: red;
}
}
}
#all { color: blue; }
#the { color: blue; }
#same { color: blue; }
ul, li, div, q, blockquote, textarea {
margin: 0;
}
td {
margin: 0;
padding: 0;
}
td, input {
line-height: 1em;
}
a {
color: red;
&:hover { color: blue; }
div & { color: green; }
p & span { color: yellow; }
}
.foo {
.bar, .baz {
& .qux {
display: block;
}
.qux & {
display: inline;
}
.qux& {
display: inline-block;
}
.qux & .biz {
display: none;
}
}
}
.b {
&.c {
.a& {
color: red;
}
}
}
.b {
.c & {
&.a {
color: red;
}
}
}
.p {
.foo &.bar {
color: red;
}
}
.p {
.foo&.bar {
color: red;
}
}
.foo {
.foo + & {
background: amber;
}
& + & {
background: amber;
}
}
.foo, .bar {
& + & {
background: amber;
}
}
.foo, .bar {
a, b {
& > & {
background: amber;
}
}
}
.other ::fnord { color: red }
.other::fnord { color: red }
.other {
::bnord {color: red }
&::bnord {color: red }
}
// selector interpolation
@theme: blood;
@selector: ~".@{theme}";
@{selector} {
color:red;
}
@{selector}red {
color: green;
}
.red {
#@{theme}.@{theme}&.black {
color:black;
}
}
@num: 3;
:nth-child(@{num}) {
selector: interpolated;
}
.test {
&:nth-child(odd):not(:nth-child(3)) {
color: #ff0000;
}
}
[prop],
[prop=10%],
[prop="value@{num}"],
[prop*="val@{num}"],
[|prop~="val@{num}"],
[*|prop$="val@{num}"],
[ns|prop^="val@{num}"],
[@{num}^="val@{num}"],
[@{num}=@{num}],
[@{num}] {
attributes: yes;
}
/*
Large comment means chunk will be emitted after } which means chunk will begin with whitespace...
blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank blank
*/
@{selector} {
color: red;
}
~~~~~~~~~~

View file

@ -0,0 +1,58 @@
#strings {
background-image: url("http://son-of-a-banana.com");
quotes: "~" "~";
content: "#*%:&^,)!.(~*})";
empty: "";
brackets: "{" "}";
escapes: "\"hello\" \\world";
escapes2: "\"llo";
}
#comments {
content: "/* hello */ // not-so-secret";
}
#single-quote {
quotes: "'" "'";
content: '""#!&""';
empty: '';
semi-colon: ';';
}
#escaped {
filter: ~"DX.Transform.MS.BS.filter(opacity=50)";
}
#one-line { image: url(http://tooks.com) }
#crazy { image: url(http://), "}", url("http://}") }
#interpolation {
@var: '/dev';
url: "http://lesscss.org@{var}/image.jpg";
@var2: 256;
url2: "http://lesscss.org/image-@{var2}.jpg";
@var3: #456;
url3: "http://lesscss.org@{var3}";
@var4: hello;
url4: "http://lesscss.org/@{var4}";
@var5: 54.4px;
url5: "http://lesscss.org/@{var5}";
}
// multiple calls with string interpolation
.mix-mul (@a: green) {
color: ~"@{a}";
}
.mix-mul-class {
.mix-mul(blue);
.mix-mul(red);
.mix-mul(black);
.mix-mul(orange);
}
@test: Arial, Verdana, San-Serif;
.watermark {
@family: ~"Univers, @{test}";
family: @family;
}
~~~~~~~~~~

View file

@ -0,0 +1,21 @@
@Eight: 8;
@charset "UTF-@{Eight}";
@ns: less;
@namespace @ns "http://lesscss.org";
@name: enlarger;
@keyframes @name {
from {font-size: 12px;}
to {font-size: 15px;}
}
.m(reducer);
.m(@name) {
@-webkit-keyframes @name {
from {font-size: 13px;}
to {font-size: 10px;}
}
}
~~~~~~~~~~

View file

@ -0,0 +1,84 @@
@a: 2;
@x: (@a * @a);
@y: (@x + 1);
@z: (@x * 2 + @y);
@var: -1;
.variables {
width: (@z + 1cm); // 14cm
}
@b: @a * 10;
@c: #888;
@fonts: "Trebuchet MS", Verdana, sans-serif;
@f: @fonts;
@quotes: "~" "~";
@q: @quotes;
@onePixel: 1px;
.variables {
height: (@b + @x + 0px); // 24px
color: @c;
font-family: @f;
quotes: @q;
}
.redef {
@var: 0;
.inition {
@var: 4;
@var: 2;
three: @var;
@var: 3;
}
zero: @var;
}
.values {
minus-one: @var;
@a: 'Trebuchet';
@multi: 'A', B, C;
font-family: @a, @a, @a;
color: @c !important;
multi: something @multi, @a;
}
.variable-names {
@var: 'hello';
@name: 'var';
name: @@name;
}
.alpha {
@var: 42;
filter: alpha(opacity=@var);
}
.polluteMixin() {
@a: 'pollution';
}
.testPollution {
@a: 'no-pollution';
a: @a;
.polluteMixin();
a: @a;
}
.units {
width: @onePixel;
same-unit-as-previously: (@onePixel / @onePixel);
square-pixel-divided: (@onePixel * @onePixel / @onePixel);
odd-unit: unit((@onePixel * 4em / 2cm));
percentage: (10 * 50%);
pixels: (50px * 10);
conversion-metric-a: (20mm + 1cm);
conversion-metric-b: (1cm + 20mm);
conversion-imperial: (1in + 72pt + 6pc);
custom-unit: (42octocats * 10);
custom-unit-cancelling: (8cats * 9dogs / 4cats);
mix-units: (1px + 1em);
invalid-units: (1px * 1px);
}
~~~~~~~~~~

View file

@ -0,0 +1,43 @@
.whitespace
{ color: white; }
.whitespace
{
color: white;
}
.whitespace
{ color: white; }
.whitespace{color:white;}
.whitespace { color : white ; }
.white,
.space,
.mania
{ color: white; }
.no-semi-column { color: white }
.no-semi-column {
color: white;
white-space: pre
}
.no-semi-column {border: 2px solid white}
.newlines {
background: the,
great,
wall;
border: 2px
solid
black;
}
.empty {
}
.sel
.newline_ws .tab_ws {
color:
white;
background-position: 45
-23;
}
~~~~~~~~~~