├── .envrc ├── .github ├── ISSUE_TEMPLATE │ ├── defect.md │ └── enhancement.md ├── pull_request_template.md └── workflows │ ├── ci.yml │ └── pursuit.yml ├── .gitignore ├── .tidyrc.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── book.toml ├── bower.json ├── docs ├── README.md ├── SUMMARY.md └── overview │ ├── animations.md │ ├── calc.md │ ├── rendering.md │ ├── selectors.md │ ├── syntax.md │ └── values.md ├── example.dhall ├── examples ├── README.md ├── ZenGarments.purs ├── output │ └── ZenGarments.css └── type-errors │ ├── CursorMissingFallback.purs │ ├── FontFaceMissingFontFamily.purs │ ├── FontFaceMissingSrc.purs │ ├── FontFamilyMissingFallback.purs │ ├── GradientConsecutiveTransitionHints.purs │ ├── GradientInsufficientColorStops.purs │ ├── GridTemplateColumnsAutoRepeatWithFlex.purs │ ├── GridTemplateColumnsMultipleAutoRepeat.purs │ ├── KeyframesNonAnimatableProperty.purs │ ├── README.md │ ├── RulesetPropertyAppearsTwice.purs │ ├── SelectorMultiplePseudoElements.purs │ ├── SelectorPseudoClassingPseudoElement.purs │ ├── SelectorPseudoElementDescendant.purs │ └── TransitionPropertyNotAnimatable.purs ├── meta └── test-count.json ├── package-lock.json ├── package.json ├── packages.dhall ├── scripts ├── check-examples.mjs └── docs-tryps.mjs ├── shell.nix ├── spago.dhall ├── src ├── Tecton.purs └── Tecton │ ├── Internal.js │ ├── Internal.purs │ └── Rule.purs ├── test.dhall └── test ├── AlignSpec.purs ├── AnimationsSpec.purs ├── BackgroundsSpec.purs ├── BoxSpec.purs ├── ColorSpec.purs ├── ContentSpec.purs ├── DisplaySpec.purs ├── FlexboxSpec.purs ├── FontsSpec.purs ├── GridSpec.purs ├── ImagesSpec.purs ├── InlineSpec.purs ├── ListsSpec.purs ├── Main.purs ├── MaskingSpec.purs ├── MediaQueriesSpec.purs ├── OverflowSpec.purs ├── PositionSpec.purs ├── RenderSpec.purs ├── SelectorsSpec.purs ├── SizingSpec.purs ├── TextDecorSpec.purs ├── TextSpec.purs ├── TransformsSpec.purs ├── TransitionsSpec.purs ├── UISpec.purs ├── UnsafeDeclarationSpec.purs ├── Util.purs ├── VisufxSpec.purs ├── VisurenSpec.purs └── WritingModesSpec.purs /.envrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if command -v lorri; 4 | then 5 | eval "$(lorri direnv)" 6 | elif command -v nix-shell 7 | then 8 | use nix 9 | fi 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/defect.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Defect 3 | about: Report a defect to help us improve 4 | title: '' 5 | labels: 'defect' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Description 11 | 12 | Provide a clear and concise description of what the defect is. 13 | 14 | ### Input 15 | 16 | To help us reproduce the issue, include the code (i.e. the PureScript "style sheet") you wrote. If possible, please provide the most minimal example that demonstrates the problem. 17 | 18 | ### Expected output 19 | 20 | Please show an example of the output you expected and explain. 21 | 22 | ### Actual result 23 | 24 | If you encountered an unexpected compiler error, please share it here and explain why your input was valid. 25 | 26 | If your code compiled but the CSS output was incorrect, please include the output and explain why it is incorrect. 27 | 28 | ### References 29 | 30 | Please link to a [W3C specification](https://www.w3.org/TR/css-2021/) (preferred), [MDN](https://developer.mozilla.org) article, or other source that supports your findings. 31 | 32 | ### Additional details 33 | 34 | Please share any additional details or thoughts here, if applicable. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement 3 | about: Request an improvement or new feature. 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Problem 11 | Describe the problem you are trying to solve, just in case we might be able to offer an existing solution. 12 | 13 | ### Requested enhancement 14 | What would you like us to add or improve? 15 | 16 | ### References 17 | Please link to the relevant [W3C specification](https://www.w3.org/TR/css-2021/) (preferred), [MDN](https://developer.mozilla.org) article, or other reference material related to your request, if applicable. 18 | 19 | ### Anything else? 20 | Provide any additional context or details you think might be helpful. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | Provide an overview of the change. 4 | 5 | ### Design considerations 6 | 7 | Share any design considerations you feel are important. 8 | 9 | ### Future plans 10 | 11 | If applicable, please share any ideas you may have to improve upon your submission in the future. 12 | 13 | ### References 14 | 15 | Provide links to related issues, [W3C specifications](https://www.w3.org/TR/css-2021/), [MDN](http://developer.mozilla.org) articles, or other supporting resources. 16 | 17 | ### Code change checklist 18 | 19 | - [ ] Any new or updated functionality includes corresponding unit test coverage. 20 | - [ ] I have verified code formatting, run the unit tests, and checked for any changes in the examples. 21 | - [ ] I have added an entry to the _Unreleased_ section of the CHANGELOG. 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - 'docs/**/*.md' 8 | - 'examples/**/*.purs' 9 | - 'examples/**/*.js' 10 | - 'src/**/*.purs' 11 | - 'src/**/*.js' 12 | - 'test/**/*.purs' 13 | - 'test/**/*.js' 14 | - '*.json' 15 | - '*.dhall' 16 | - 'scripts/check-docs.mjs' 17 | - 'scripts/check-examples.mjs' 18 | - '.github/workflows/ci.yml' 19 | pull_request: 20 | branches: [master] 21 | paths: 22 | - 'docs/**/*.md' 23 | - 'examples/**/*.purs' 24 | - 'examples/**/*.js' 25 | - 'src/**/*.purs' 26 | - 'src/**/*.js' 27 | - 'test/**/*.purs' 28 | - 'test/**/*.js' 29 | - '*.json' 30 | - '*.dhall' 31 | - 'scripts/check-docs.mjs' 32 | - 'scripts/check-examples.mjs' 33 | - '.github/workflows/ci.yml' 34 | 35 | jobs: 36 | build: 37 | name: Build 38 | 39 | runs-on: ubuntu-latest 40 | 41 | steps: 42 | - uses: actions/checkout@v3 43 | 44 | - name: Set up a PureScript toolchain 45 | uses: purescript-contrib/setup-purescript@main 46 | with: 47 | purescript: "unstable" 48 | purs-tidy: "latest" 49 | 50 | - name: Cache PureScript dependencies 51 | uses: actions/cache@v2 52 | with: 53 | key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }} 54 | path: | 55 | .spago 56 | output 57 | 58 | - name: Cache NPM dependencies 59 | uses: actions/cache@v2 60 | with: 61 | key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} 62 | path: node_modules 63 | 64 | - name: Install PureScript dependencies 65 | run: spago install 66 | 67 | - name: Build source 68 | run: spago build --no-install --purs-args '--censor-lib --strict' 69 | 70 | - name: Run tests 71 | run: spago -x test.dhall test 72 | 73 | - name: Check formatting 74 | run: purs-tidy check examples src test 75 | 76 | - name: Install Node.js dependencies 77 | run: npm install 78 | 79 | - name: Check examples 80 | run: npm run check-examples -- --color 81 | 82 | - name: Check docs 83 | run: npm run check-docs 84 | 85 | - name: Verify Bower & Pulp 86 | run: | 87 | npm install bower pulp@16.0.0-0 88 | npx bower install 89 | npx pulp build -- --censor-lib --strict 90 | 91 | - if: github.ref == 'refs/heads/master' 92 | name: Dispatch tecton-halogen CI 93 | run: | 94 | curl -X POST https://api.github.com/repos/nsaunders/purescript-tecton-halogen/dispatches \ 95 | -H 'Accept: application/vnd.github.everest-preview+json' \ 96 | -u ${{ secrets.GH_ACCESS_TOKEN }} \ 97 | --data '{"event_type": "dependency_changed", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}' 98 | 99 | - if: github.ref == 'refs/heads/master' 100 | id: count 101 | name: Get test count 102 | run: echo "value=$(spago -x test.dhall test | grep Summary -A1 | tail -n 1 | cut -d "/" -f2 | cut -d " " -f1)" >> $GITHUB_OUTPUT 103 | 104 | - if: github.ref == 'refs/heads/master' 105 | name: Update test count data 106 | run: echo '{"schemaVersion":1,"label":"tests","message":"${{ steps.count.outputs.value }}","color":"cd523e"}' > meta/test-count.json 107 | 108 | - if: github.ref == 'refs/heads/master' 109 | name: Commit test count data update 110 | uses: EndBug/add-and-commit@v9 111 | with: 112 | add: meta/test-count.json 113 | author_name: GitHub Actions 114 | author_email: 41898282+github-actions[bot]@users.noreply.github.com 115 | message: Update test count. 116 | 117 | - if: github.ref == 'refs/heads/master' 118 | name: Setup mdBook 119 | uses: peaceiris/actions-mdbook@v1 120 | with: 121 | mdbook-version: "0.4.12" 122 | 123 | - if: github.ref == 'refs/heads/master' 124 | name: Build docs 125 | run: mdbook build 126 | 127 | - if: github.ref == 'refs/heads/master' 128 | name: Deploy docs 129 | uses: peaceiris/actions-gh-pages@v3 130 | with: 131 | github_token: ${{ secrets.GITHUB_TOKEN }} 132 | publish_dir: ./book -------------------------------------------------------------------------------- /.github/workflows/pursuit.yml: -------------------------------------------------------------------------------- 1 | name: Pursuit 2 | 3 | # This workflow can be used whenever publishing the docs to Pursuit fails (as it 4 | # often does via registry). 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | type: string 11 | required: true 12 | description: "The version number to publish, e.g. 0.2.1" 13 | 14 | jobs: 15 | publish: 16 | name: Publish 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | with: 23 | ref: v${{ github.event.inputs.version }} 24 | 25 | - name: Set up Bower and Pulp 26 | run: npm install -g purescript bower pulp@16.0.0-0 27 | 28 | - name: Install Bower dependencies 29 | run: bower install 30 | 31 | - name: Pulp login 32 | run: echo "${{ secrets.GH_ACCESS_TOKEN }}" | pulp login 33 | 34 | - name: Pulp publish 35 | uses: nick-fields/retry@v2 36 | with: 37 | timeout_seconds: 15 38 | max_attempts: 3 39 | command: yes | pulp publish --no-push 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /book 2 | /bower_components/ 3 | /node_modules/ 4 | /.pulp-cache/ 5 | /output/ 6 | /generated-docs/ 7 | /.psc-package/ 8 | /.psc* 9 | /.purs* 10 | /.psa* 11 | /.spago 12 | -------------------------------------------------------------------------------- /.tidyrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "importSort": "ide", 3 | "importWrap": "auto", 4 | "indent": 2, 5 | "operatorsFile": null, 6 | "ribbon": 1, 7 | "typeArrowPlacement": "first", 8 | "unicode": "never", 9 | "width": 80 10 | } 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Notable changes are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## [Unreleased] 6 | 7 | Breaking changes: 8 | 9 | New features: 10 | 11 | Bugfixes: 12 | 13 | Other improvements: 14 | 15 | ## [0.2.1] - 2023-06-12 16 | 17 | New features: 18 | - The `LineName` data constructor is now exported, replacing the `lineName` function which is now deprecated. nsaunders/purescript-tecton#44 19 | 20 | ## [0.2.0] - 2023-05-16 21 | 22 | Breaking changes: 23 | - _For pseudo-elements only_, the `&:` operator has been replaced by `&::`. Pseudo-classes continue to work with the `&:` operator. nsaunders/purescript-tecton#33 24 | - The `keyframesName` function has been dropped. Just use the `KeyframesName` constructor instead. (nsaunders/purescript-tecton#34) 25 | - The `CustomAttribute` type and `att` constructor function have been removed in favor of the [`AttrName` type from `web-html`](https://pursuit.purescript.org/packages/purescript-web-html/4.1.0/docs/Web.HTML.Common#t:AttrName). nsaunders/purescript-tecton#35 26 | - The `&.` operator (`byClass` function) no longer accepts a string argument. Instead, it requires a [`ClassName`](https://pursuit.purescript.org/packages/purescript-web-html/4.1.0/docs/Web.HTML.Common#t:ClassName). nsaunders/purescript-tecton#35 27 | - The `&#` operator (`byId` function) no longer accepts a string argument. Instead, it requires a value of the newly-added `ElementId` type. nsaunders/purescript-tecton#35, nsaunders/purescript-tecton#37 28 | - The `nth` function has been dropped, replaced by the `#+` and `#-` operators that can be used to construct **a**_n_+**b** formulas. nsaunders/purescript-tecton#36 29 | 30 | New features: 31 | - `box-sizing` property nsaunders/purescript-tecton#31 32 | - `cursor` property nsaunders/purescript-tecton#41 33 | - `word-break` property nsaunders/purescript-tecton#32 34 | - Support for custom pseudo-classes and pseudo-elements via the `PseudoClass` and `PseudoElement` constructors nsaunders/purescript-tecton#38 35 | - A new `unsafeDeclaration` function offers an "escape hatch" for e.g. vendor-prefixed or experimental properties that haven't been added to the library yet. nsaunders/purescript-tecton#40 36 | 37 | Bugfixes: 38 | - Fixed the content of the compiler error that results from duplicate properties or descriptors within a single ruleset. Previously all values were incorrectly reported as having the type `CommonKeyword`. nsaunders/purescript-tecton#39 39 | 40 | ## [0.1.6] - 2022-12-12 41 | 42 | New features: 43 | - `:focus-within` pseudo-class nsaunders/purescript-tecton#17 44 | - `appearance` property nsaunders/purescript-tecton#18 45 | 46 | Other improvements: 47 | - Examples and tests are now formatted using `purs-tidy`. 48 | 49 | ## [0.1.5] - 2022-12-05 50 | 51 | New features: 52 | - Added the `Declarations` type alias. 53 | 54 | ## [0.1.4] - 2022-11-29 55 | 56 | New features: 57 | - Grid support / new CSS properties nsaunders/purescript-tecton#12 58 | - `grid-auto-columns` 59 | - `grid-auto-flow` 60 | - `grid-auto-rows` 61 | - `grid-column-end` 62 | - `grid-column-start` 63 | - `grid-row-end` 64 | - `grid-row-start` 65 | - `grid-template-columns` 66 | - `grid-template-rows` 67 | - Flexbox properties extended to support additional values defined in 68 | [Box Alignment Module Level 3](https://www.w3.org/TR/css-align-3) 69 | - `justify-content` 70 | - `align-content` 71 | - `justify-self` 72 | - `align-self` 73 | - `justify-items` 74 | - `align-items` 75 | 76 | Other improvements: 77 | - New examples of type safety nsaunders/purescript-tecton#10 78 | - [`TypeError.SelectorPseudoClassingPseudoElement`](examples/type-errors/SelectorPseudoClassingPseudoElement.purs) 79 | - [`TypeError.SelectorPseudoElementDescendant`](examples/type-errors/SelectorPseudoElementDescendant.purs) 80 | - [`TypeError.GridTemplateColumnsAutoRepeatWithFlex`](examples/type-errors/GridTemplateColumnsAutoRepeatWithFlex.purs) 81 | - [`TypeError.GridTemplateColumnsMultipleAutoRepeat`](examples/type-errors/GridTemplateColumnsMultipleAutoRepeat.purs) 82 | - The [`check-examples`](scripts/check-examples.mjs) script now verifies that each `TypeError.*` example fails 83 | to compile. 84 | 85 | ## [0.1.3] - 2022-11-09 86 | 87 | Other improvements: 88 | - Performance optimizations nsaunders/purescript-tecton#9 89 | - Dropped `arrays` dependency 90 | 91 | ## [0.1.2] - 2022-11-06 92 | 93 | New features: 94 | - A single property appearing more than once within a ruleset now results in a compiler error. nsaunders/purescript-tecton#7 95 | - A new type alias `CSS` provides a way to annotate style sheet values without using internal types. nsaunders/purescript-tecton#8 96 | 97 | ## [0.1.1] - 2022-11-03 98 | 99 | New features: 100 | - Declarations are now guaranteed to be rendered in the input order. nsaunders/purescript-tecton#6 101 | 102 | ## [0.1.0] - 2022-10-14 103 | 104 | Initial release 105 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Welcome, and thanks for your interest in Tecton. Whether you are new to CSS and PureScript or here to teach us new tricks, we're glad to have you and look forward to your contributions. Listed below are a few ways you can help. 5 | 6 | Asking questions 7 | ---------------- 8 | If you have a question, please [open an issue](https://github.com/nsaunders/purescript-tecton/issues/new?labels=question) to discuss it. This might help us to find a defect; reveal an opportunity to improve the documentation or developer experience; or help someone facing a similar issue in the future. 9 | 10 | Reporting defects 11 | ----------------- 12 | Please [open an issue](https://github.com/nsaunders/purescript-tecton/issues/new?labels=defect&template=defect.md) to discuss any defects you find. In addition to a brief description, include 13 | * the code you wrote; 14 | * the expected output; 15 | * the compiler error or incorrect output you encountered; and 16 | * if possible, a link to the relevant W3C specification, which can be found [here](https://www.w3.org/TR/css-2021/) or via Google search. 17 | 18 | Improving documentation 19 | ----------------------- 20 | Please share any suggestions for improving or adding to the documentation by [submitting a pull request](https://github.com/nsaunders/purescript-tecton/compare) or [opening an issue](https://github.com/nsaunders/purescript-tecton/issues/new?labels=documentation). 21 | 22 | Sharing resources 23 | ----------------- 24 | If you have created a tutorial, auxiliary library, interesting code example, or other resource related to Tecton, please [share it with the PureScript community](https://discourse.purescript.org). Increasing awareness will bring more users (and potential contributors) to the project. 25 | 26 | Submitting code 27 | --------------- 28 | Pull requests are welcome, but we ask that you [open an issue](https://github.com/nsaunders/purescript-tecton/issues/new) to discuss your plans before making any significant investment of your valuable time. 29 | 30 | ### Development environment 31 | 32 | You can use [Nix](https://github.com/NixOS/nix) to create a development environment with all required tooling. Simply run `nix-shell` in the root project directory, and you'll have everything you need to get started. 33 | 34 | ### Making changes 35 | 36 | The `src` directory includes the following modules: 37 | * [`Tecton`](./src/Tecton.purs), which exports most of the public API; 38 | * [`Tecton.Internal`](./src/Tecton/Internal.purs), which contains internal and private functions and data types; and 39 | * [`Tecton.Rule`](./src/Tecton/Rule.purs), for use with qualified-do syntax to create rulesets (not much to see here). 40 | 41 | Usually your work will begin in the `Tecton.Internal` module. From this module, export the minimum functions, types, and classes required for client code to compile. Then, from the `Tecton` module, re-export only the subset that client code should interact with directly. This architecture clearly defines the public API while allowing implementation details to remain flexible. 42 | 43 | After making any changes, run `purs-tidy format-in-place examples src test` to format the code. 44 | 45 | Unit test(s) should accompany each change. Ensure that the system under test (e.g. the new property binding you added) is imported from the `Tecton` module so that tests accurately represent client code. Tests are organized into modules corresponding to W3C CSS specifications, which can be found [here](https://www.w3.org/TR/css-2021/), and almost always follow this format: 46 | 47 | ```purescript 48 | "margin:0" `isRenderedFrom` margin := nil 49 | -- ^ expected output ^ given this input 50 | ``` 51 | 52 | The `isRenderedFrom` utility, imported from `Test.Util`, provides an appropriate test description and the corresponding assertion automatically. 53 | 54 | ### Preparing your submission 55 | 56 | Please verify your changes before submitting a pull request using the following commands: 57 | 1. `purs-tidy check src`, which ensures that modules in the `src` directory conform to the project's formatting standards; 58 | 1. `spago -x test.dhall test`, which runs the unit tests; and 59 | 1. `npm i && npm run check-examples`, which (as a basic sanity check) compares the output of each [example](./examples) to a previous snapshot. 60 | 61 | > ℹ️ **NOTE**: If appropriate, running `npm run check-examples -- --update` will update the example output snapshots. 62 | 63 | ### Submitting a pull request 64 | 65 | When you are ready to [submit a pull request](https://github.com/nsaunders/purescript-tecton/compare), please make sure to include: 66 | 1. a brief description of the change; 67 | 1. a link to the related issue, if applicable; 68 | 1. a link to any relevant [W3C specifications](https://www.w3.org/TR/css-2021/) 69 | 1. any design considerations you feel are important; and 70 | 1. any ideas you may have to improve upon your submission in the future. 71 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Contributors 2 | ------------ 3 | 4 | In addition to those listed on the [Contributors](https://github.com/nsaunders/purescript-tecton/graphs/contributors) page, [Thomas Honeyman](https://github.com/thomashoneyman) and [Nate Faubion](https://github.com/natefaubion) provided key design input and generous mentorship to the primary author. 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nick Saunders 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tecton 2 | 3 | [![CI](https://github.com/nsaunders/purescript-tecton/workflows/CI/badge.svg?branch=master)](https://github.com/nsaunders/purescript-tecton/actions?query=workflow%3ACI+branch%3Amaster) [![Latest release](http://img.shields.io/github/release/nsaunders/purescript-tecton.svg)](https://github.com/nsaunders/purescript-tecton/releases) [![PureScript registry](https://img.shields.io/badge/dynamic/json?color=informational&label=registry&query=%24.tecton.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Fpurescript%2Fpackage-sets%2Fmaster%2Fpackages.json)](https://github.com/purescript/registry) [![purescript-tecton on Pursuit](https://pursuit.purescript.org/packages/purescript-tecton/badge)](https://pursuit.purescript.org/packages/purescript-tecton) [![Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/nsaunders/purescript-tecton/master/meta/test-count.json)](./test) 4 | 5 | Tecton is a domain-specific language for authoring CSS, embedded in PureScript. The unique capabilities of PureScript allow Tecton's strongly-typed interface to guard against a wide range of [errors](examples/type-errors) while remaining highly flexible like vanilla CSS. 6 | 7 | [![Live Demo](https://shields.io/badge/-Live%20Demo-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAWQIYEsB2cDuALGAnGAKEJWAAcQ8AXOABQKgjCJPMpoFEAzLmAYxoAKbrwEBKVhWpwAEkiggA5jAxIAzrMntZ8pSoB0MgCoIAMnHWyZW6XIXK0hk6f30QZfFRQwNlmbRsaOz1HADUAERBgfXCAeQR9AGUqPHRFACUVZgI8OEECNGyxCw1gh0C4IzwAT1oIAkS%2BVLIhAqKSypqKgFU0NSRefQBhEHw%2BeEEIPoGYEbGYCQqRhVz8xQAjCVIpGnCkKiR9IwgyWH0AOR8qGDA8wQB6AB0xRe3tI34qEAxBeRRFNAASWuwDUABoLGhSPsUN9wvUYd8IUgocBEUDrnh0SMplRkaj0eckMAYPjoV5vkZSGkAGJTASwtAQ9ZIPgAa0UeBAUzAy0ozMo2XSSDAKAg4Lg60F%2BGS1VgAoAHolsCKQJgIeM0JiNSAVhDmIp9Sg1KckNUIVxYAqLSA%2BOKbVqacSUFBzXAuN8qIkUAAvUnuz0AdRg-2weLgiiQZAhuFD4ewIAAbvgIeguOgUNdU2goOgYDSrRCAFbirxcaojLUqcNsmDVLhYkkS2v1xs%2BIkkiG5tAwGQhxRhrt5pB4CFovCKdAQqFQafff3cqjdmCy%2BVwMgi0VoQ3rgQQsgEKhUN1ka1wNoy3AwcNcg5ZuD9PqJfAoLgQtT8CHXBVUcL8SjoqYeZflifQengwAQlMKDJng-SzlgKBgFQ2AQoIABkABcYhoQA-DheSYQAvARggAH4vBUHwCN8hi6A4dzofolFvNI1FfI46TQPAlhcbAxBougcCYZhcAiJ8cC9JmhCCRgRGdNU%2BgXrkAAkcBgLacB8GoGhhsAUCEHAWC4AQhlGVpnqoD2uTyTIwxQOoagdvAABEfCWXmeAuWZRnrBAR7fHAtn2Y5zlwC5fkBWg3nmQ%2BZBCcFQwOTpYUuSa6AxeZUxJaFxITHZOUpXlcAKsU8kKj5cCVelaAAIIEhSclwC2DZ5U5xVpfF0XELF2kaPJGmVZVRnQbB8FwExFlalZ%2BBwLhcB8TA%2BiDbFsWYEhKHCfJp5wAATAADPtw3mbGA40MR64Knth3Hb5rIclyPJ8rkF0TuscAAIwAKyfQAHHtADMt3qcaprVFt7pWsDfwAsCMCghDmqYsDJZqGWFaetWiPVvgx3HS1bYaDV9Xkoyc3qSAwNkAIcD7eTi3LZTq2rSkKJqOBwAQ7e%2BzwDOVM0x9h309xjPA0ZrNgZQnOvSAd4TAacAAwAbPtEjM8do34ONk01cLZwrczkKk98YUXcTDWMtDFtwgijUQx%2BfCfVbxsYvg2LclqENphm1zO2ijVUsAtL0nbF3LiOwMa1CY3yBN%2BiSv5HF60tBvM4uy6rvAF1oPOwPfr%2B-5Yo1QE9hDOc9sDUp4Nkmdl7nhuSiASoqhpmBcxscBfR9ndK3ASu7XAZFwDOg-Dy6o8j0PO0fZX92ch7vK6pQ7fvQDdMAwALHAG9HQ37krCvn0AJxbwdAN7R9u2V9KeDCqK4oQztSvAx6jrOq6EMuQAtFGpwwF-ahqho3hi5OATxwoACFuxsmQHwRIQCQQ0k9KA8BLlnyKFGJJQEKDHjhXOLLEAcBEhsxweFPsUBkxeD4EgUhLlaqpHkKQx8ahnypC4C-T03o-SPyuh9DeHCtTBjjBDL6N0G7Lj7MIi6M8G6ihNA5cGF10DLgLDACqDcYauwRhdJGuMG6RjIDwuAv1gbjknBgbOLoqabjSEYreU9eFXwblHGCWtY6TUiknLCcAEywWTqLBuLJ2TzyekvF68k3rbwHhvAA7J3IGgSm7KlVG3V6HdL7-Q%2BgDOJfD-pD0nmPKAE9x4OKdi4mORSPGJ0Ct4j0doNDzQZqnVaVchQijFBoSxBlEnNxSYfGJmSvq912l9H6%2BSSmFOKUU0pV9Kp6SKURY6Mg7KikTMDAA2v4fQfBkpqAAPpTQOJ5AAuhsqw%2BhPHfDFnAdZ5z9gpDyHZWqR48CpW%2BDslA7IXLFDUi5FCxp9DpjglQDgsASRaiGNgF0YBtm7KAmjfQm5BAAHJQEAB4AB8cBsq7LCrrTF4VkViAANwfioIHGAi5BCCDKli-5ahAUoGBaC%2BG1ZIXQthY5eFVAlLwyTDAFF6KsU4tyiSOKQkCUuSJRCURh0xCZQbhCLZOzHIHMuWga5pyG5GVucs-QMAwVY0EHZFlwBUpqETIob51ydV3JeY8-QzyUipXWshbA3zwofV%2Bgq7VSq7L3NWE8l5qVTphg9S5L1PrFV2oecax1waOqJm8JgCBTdw10zprtU%2BG8o2Gy1dq21eqDWss9nG01qUNwoWtQW2Kur-X2rjU615HUwDhoQJfbeoQPqmH%2Bl9KAW8N6hCVnwAG%2BgAbdzpsM-QSsj59znXTD6%2Bh9ofS-voEZn19BHxiauuJu19C-SgIujevcj1KyGB9I%2B%2Bgvpb2%2Bku8%2BB1Pqjq%2Bruhdu0%2BD7S-hvfQG9dpfyfb9L%2BAGgM%2BmAAujefA-1jonV-JWX890zpgzB2mX9F3LrXV9FDm6Yn6G3Xu36pgvprq3thmJG8hhfuPcYrD29Pr7W-efG9b66YUYHv%2B4x7HfqJgBv2z9vGN5cZ9Lm6NeqA0OqbaldMUAoDhrtHgAoVBnpCbzTa8y6z80FvU4bP1%2Bh87hXgWgPgSmjKadiqcyqGlHZ9UeXZYwZg4AGrKlHfovA5h4HGHAFSwMXJorRnKGAGKhXniyJeGA151yHmPFpHScBJVovuL52AAXrkErKAYZScAkLVkzODA1hAgA) 8 | 9 | ## Quick example 10 | 11 | ### Input 12 | ```purescript 13 | body ? Rule.do 14 | width := pct 100 15 | height := pct 100 16 | padding := px 16 ~ px 32 17 | 18 | media screen { minWidth: px 768 } ? 19 | body ? 20 | padding := pct 5 ~ pct 10 21 | ``` 22 | 23 | ### Output 24 | ```css 25 | body { 26 | width: 100%; 27 | height: 100%; 28 | padding: 16px 32px; 29 | } 30 | @media screen and (min-width: 768px) { 31 | body { 32 | padding: 5% 10%; 33 | } 34 | } 35 | ``` 36 | 37 | Many more examples are located in the [examples](./examples) and [test](./test) 38 | subdirectories. 39 | 40 | ## Installation 41 | 42 | The preferred installation method is [Spago](https://github.com/purescript/spago). 43 | 44 | ```sh 45 | spago install tecton 46 | ``` 47 | 48 | ## Related 49 | 50 | * [purescript-tecton-halogen](https://github.com/nsaunders/purescript-tecton-halogen) 51 | * [purescript-tecton-halogen-starter](https://github.com/nsaunders/purescript-tecton-halogen-starter) 52 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | language = "en" 3 | multilingual = false 4 | src = "docs" 5 | title = "Tecton Guide" 6 | [output.html] 7 | git-repository-url = "https://github.com/nsaunders/purescript-tecton" -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "purescript-tecton", 3 | "license": [ 4 | "MIT" 5 | ], 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/nsaunders/purescript-tecton" 9 | }, 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "output" 15 | ], 16 | "dependencies": { 17 | "purescript-arrays": "^v7.0.0", 18 | "purescript-colors": "^v7.0.1", 19 | "purescript-either": "^v6.0.0", 20 | "purescript-foldable-traversable": "^v6.0.0", 21 | "purescript-integers": "^v6.0.0", 22 | "purescript-lists": "^v7.0.0", 23 | "purescript-numbers": "^v9.0.0", 24 | "purescript-prelude": "^v6.0.0", 25 | "purescript-record": "^v4.0.0", 26 | "purescript-strings": "^v6.0.0", 27 | "purescript-transformers": "^v6.0.0", 28 | "purescript-tuples": "^v7.0.0", 29 | "purescript-web-html": "^v4.0.0" 30 | } 31 | } -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Tecton: CSS in PureScript 2 | 3 | Tecton is a domain-specific language for authoring CSS using [PureScript](https://purescript.org/). At a basic level, it could be compared to CSS preprocessors such as [Sass](http://sass-lang.com/) and [LESS](http://lesscss.org/). However, where these preprocessors aim to add expressivity to CSS, Tecton offers the full expressive power of its host language along with a high degree of type safety. It also unlocks a number of secondary benefits, such as reuse of existing PureScript knowledge and seamless colocation with related markup. If you're ready to write masterful CSS with Tecton, let's get started. 4 | 5 | ## Installation 6 | 7 | ### Local installation 8 | 9 | The preferred installation method is [Spago](https://github.com/purescript/spago): 10 | 11 | ```bash 12 | spago install tecton 13 | ``` 14 | 15 | ### Try PureScript 16 | 17 | Alternatively, to evaluate Tecton without installing anything locally, you can use [Try PureScript](https://try.purescript.org). Throughout the documentation, look for buttons like this one to launch the corresponding code example in the Try PureScript app: 18 | 19 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](javascript:alert("Normally%2C%20you%27ll%20be%20redirected%20to%20Try%20PureScript.%20For%20now%2C%20let%27s%20continue%20getting%20started%20with%20Tecton.")) 20 | 21 | You can give this a try now with the following example. 22 | 23 | ## "Hello world" example 24 | 25 | This example serves as a good starting point for experimenting with Tecton. Simply update the `styleSheet` function with your own rules to see Tecton's CSS output. 26 | 27 | ```haskell 28 | module Example.StyleSheet where 29 | 30 | import Color (rgb) 31 | import Data.Tuple.Nested ((/\)) 32 | import Tecton 33 | import Tecton.Rule as Rule 34 | 35 | styleSheet :: CSS 36 | styleSheet = do 37 | universal &. ClassName "hello-world" ? Rule.do 38 | width := px 400 39 | height := px 200 40 | backgroundColor := rgb 0 5 56 41 | color := rgb 232 199 148 42 | display := flex 43 | alignItems := center 44 | justifyContent := center 45 | fontFamily := "Lexend" /\ sansSerif 46 | fontSize := px 32 47 | fontWeight := 700 48 | ``` 49 | 50 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNklzgcqHY4AVoAcwAjR2NKABEUPBQsVwhMGCwAORhZPBgwBoEAegAdAyqTV3sQEWW3NZEsACVoeEi4fdgmCaJ4sgo4AC4bmpwcBnPiK8oAXmkQBjg4IuYAG55dJQOAAMiwNSgkVkIzQ8AARGQoHUALRUdhQMAIuAAfmOBywYG+v1+VGYYDwJFun3QSDgABYAAxMn6ksjMJokSg3Wn0gBMLLZvxaKHkAGsmrQQEUwLV6ry4M0WnAmXAAKwagBswrg8hAdVoNKVrTg-IAzPy4ABGACctptDIAHLqZLJMCgCMbTLAkLqUFBOSIAJKTYBRRUFESTWi6gBWEAmzFMBBy0Zg0eNUZjutM6zwADE0MwoF7FQiADIwJAZ7FwBZwdLFHB5ZO5-M4ZgAL3girpcEt7ejAHUYJzucaAOxChjAFDMES3e62CxWOCicSz+eL95smJeHx+AJ4LDBUK6tR-YoZGBlfKMUlwS9wBHadD0XTafX8XQ4vRK2s8lIchKHfChCEbQhXhAuB-1fWZvxgT9ZjA38gA) 51 | 52 | ## API documentation 53 | 54 | API documentation is available on [Pursuit](https://pursuit.purescript.org/packages/purescript-tecton). 55 | 56 | ## Troubleshooting 57 | 58 | If you encounter an error that is difficult to understand, please try the following resources: 59 | - The [test suite](https://github.com/nsaunders/purescript-tecton/tree/master/test) may offer an example of what you are trying to achieve. 60 | - The W3C publishes the [CSS specifications](https://www.w3.org/TR/?tag=css) that guide the design of this library. 61 | 62 | ## Support 63 | 64 | If you get stuck, help is available in the [PureScript Discord](https://purescript.org/chat) chat or on the [PureScript Discourse](https://discourse.purescript.org/) forum. 65 | 66 | ## Contributing 67 | 68 | Contributions are welcome. Please see the [Contributing guide](https://github.com/nsaunders/purescript-tecton/blob/master/CONTRIBUTING.md) for more information. 69 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Getting started](./README.md) 4 | - [Overview]() 5 | - [Syntax](./overview/syntax.md) 6 | - [Values and units](./overview/values.md) 7 | - [Selectors](./overview/selectors.md) 8 | - [Animations](./overview/animations.md) 9 | - [Font resources]() 10 | - [Media queries]() 11 | - [Calc expressions](./overview/calc.md) 12 | - [Rendering](./overview/rendering.md) -------------------------------------------------------------------------------- /docs/overview/animations.md: -------------------------------------------------------------------------------- 1 | # Animations 2 | 3 | CSS animations are a powerful tool for adding motion and interactivity to a webpage. They allow you to animate various properties of HTML elements, such as position, size, color, and opacity. 4 | 5 | ## Defining keyframes 6 | 7 | Animations are defined using keyframes, which specify the intermediate states of an element during the animation. Keyframe rules define the stages of an animation in terms of percent progress. Each rule indicates the value of a given CSS property at that point in the animation. 8 | 9 | For example, the following keyframes transition the background and foreground colors between black and white: 10 | 11 | ```haskell 12 | module Example.StyleSheet where 13 | 14 | import Color (black, white) 15 | import Data.Tuple.Nested ((/\)) 16 | import Tecton 17 | import Tecton.Rule as Rule 18 | 19 | styleSheet :: CSS 20 | styleSheet = do 21 | keyframes (KeyframesName "black-and-white") ? do 22 | pct 0 /\ pct 100 ? Rule.do 23 | backgroundColor := black 24 | color := white 25 | pct 50 ? Rule.do 26 | backgroundColor := white 27 | color := black 28 | ``` 29 | 30 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNklzgcqHY4AQAjKBR5AGspGnEYR2NKABEUPBQsVwhMGCwAORhZPBgwBoEAegAdAyqTV3sQEU23HZEsACVoeEi4U9gmOaJ4sgo4AC4nmpwcBlviB8oAXmkQAw4HA2jACKZaGhZg0ANJgiFQ2RTKFwABEzVabQAtCgQliuvNUeEAPwAoHAuDoKxwAAMcDWlOpAEYaXTSVdJmBARSKY1MQBzWggIpgWr1J7-DHtck8+QgOq0Z7-AmMHlUygAVjZlzOWC5Mt5AqFIrFiol1BI3QNwLlCqVcClbSYwBQzBEz1etgs1NE4gYLrdcF+5JiXh8fgCeCwwVCBrUcCKJRgZXyqop8bR2nQ9F02jl-F0qLgejgMbypHIlGzFEIcC+90rxd0meW+ZgueW1cLQA) 31 | 32 | ## Creating animation 33 | 34 | Animating elements requires at minimum a keyframe animation name and duration, for example: 35 | 36 | ```haskell 37 | module Example.StyleSheet where 38 | 39 | import Tecton 40 | import Tecton.Rule as Rule 41 | 42 | styleSheet :: CSS 43 | styleSheet = do 44 | universal ? Rule.do 45 | animationName := KeyframesName "black-and-white" 46 | animationDuration := ms 150 47 | ``` 48 | 49 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNkl2j7EBEqk1dakSwAJWh4SLh22CZZQmIyCjgALhG4LJwcBn6ieKHKAF5pEAY4OCLmADc89Kg4AH5ujqwwVfX1lDFgFDxmOoA5NHgR5YBpGAJTWmfZJ+B4AAiABGUBQ8gA1gBaK5gKE0cQwQFrC5XVi3e4iAAi3gxdVGy2AUQAjABWAAMTBuzBEo3GtgsVjgonEDGptMWKJiXh8fgCeCwwVCKPWag2xQyMDK+UYFzgYrggO06Houm08nAMF0gLgejgQrypHIlBVFEIcFmg2Nut0iu0AHoNfw1fbTdqgA) 50 | 51 | A range of properties can be used to control animations: 52 | 53 | * `animationName` specifies the name of the keyframe animation to apply. 54 | * `animationDuration` sets the duration of the animation. 55 | * `animationTimingFunction` defines the timing curve for the animation (e.g. `linear`, `easeIn`, or `easeOut`). 56 | * `animationDelay` adds a delay before the animation starts. 57 | * `animationIterationCount` sets the number of times the animation should repeat. 58 | * `animationDirection` controls whether the animation should play in the `normal` or `reverse` directions, or should `alternate` (or `alternateReverse`). 59 | * `animationFillMode` specifies how the element should be styled before and after the animation (e.g. `none`, `forwards`, `backwards` or `both`). 60 | 61 | Each of these properties also supports a list of animations by using Tecton's list syntax based on nested tuples, e.g. `animationDuration := ms 150 /\ ms 250 /\ ms 75`. 62 | 63 | ## Transitions 64 | 65 | Keyframe animations provide flexibility and control for complex, multi-step animations. For basic, one-off property changes triggered by events, transitions offer a lightweight approach with no need for a `keyframes` at-rule. Instead, you simply leverage the following properties: 66 | 67 | * `transitionProperty` specifies the property to be transitioned. 68 | * `transitionDuration` specifies the length of time over which the transition should occur. 69 | * `transitionTimingFunction` defines the timing function used for the transition. It determines how intermediate property values are calculated over time. 70 | * `transitionDelay` sets a delay before the transition starts. 71 | 72 | Like animation properties, each of the transition properties supports a list of values, e.g. `transitionProperty := width /\ height /\ padding`. 73 | 74 | For example, the following rulesets transition the background and foreground colors over a 150-millisecond period on hover: 75 | 76 | ```haskell 77 | module Example.StyleSheet where 78 | 79 | import Color (black, white) 80 | import Data.Tuple.Nested ((/\)) 81 | import Tecton 82 | import Tecton.Rule as Rule 83 | 84 | styleSheet :: CSS 85 | styleSheet = do 86 | let transitionDemo = ClassName "transition-demo" 87 | universal &. transitionDemo ? Rule.do 88 | transitionProperty := backgroundColor /\ color 89 | transitionDuration := ms 150 90 | transitionTimingFunction := ease 91 | backgroundColor := white 92 | color := black 93 | universal &. transitionDemo &: hover ? Rule.do 94 | backgroundColor := black 95 | color := white 96 | ``` 97 | 98 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNklzgcqHY4AQAjKBR5AGspGnEYR2NKABEUPBQsVwhMGCwAORhZPBgwBoEAegAdAyqTV3sQEU23HZEsACVoeEi4U9gmOaJ4sgo4AC4nmpwcBlviB8oAXmkQAw4HBYJQ8LQUMVxMxdv0YKA4P8si1ZLIpmh4AAicGQ2TQ3YAWn4oExQLgRWYADc8ukoHAAGRYOA4qF4GEiOEIgD8lzOWDAgOBwJZeLZu24IHQeUIz3+jVabQA5rQQEUwLV6ms4PIQHVaGThRDWez+t4huzZXBgFEAIwAVgADAbmUbRezXKxmCJFQAxIpWC1Pf4wSKMIVweXtZWqkIa2iWrrzZ06vWW5oKskU6m0WkMpki-Ec+EgBmvEggbNwHlXSYC52RpUqtVxtMtdrJ3X1IPUEjdJjAFBe56vWwWKxwUTiBgDoe-MkxLw+PwBPBYYKhZ1qcnFDIwMr5MNCrdwTHadD0XTaHX8XSYuB6ODrvKkciUc8UGVfe6v++6E-aZZrxgS9lnfW8gA) 99 | 100 | ## See also 101 | 102 | * [Animations spec](https://github.com/nsaunders/purescript-tecton/tree/master/test/AnimationsSpec.purs) 103 | * [CSS Animations Level 1 (W3C)](https://www.w3.org/TR/css-animations-1/#animation-fill-mode) -------------------------------------------------------------------------------- /docs/overview/calc.md: -------------------------------------------------------------------------------- 1 | # Calc expressions 2 | 3 | Calc expressions allow you to perform calculations within CSS property values, supporting a wide range of use cases such as responsive design, grid systems, and fluid typography. 4 | 5 | Several calc operators are supported: 6 | * `@+@` adds two operands, e.g. `pct 90 @+@ px 16`. 7 | * `@-@` subtracts the second operand from the first, e.g. `pct 100 - px 32`. 8 | * `@*` multiplies the first operand by the second, e.g. `px 16 @* 2`. 9 | * `*@` multiplies the first operand by the second, e.g. `2 *@ px 16`. 10 | * `@/` divides the first operand by the second, e.g. `pct 100 @/ 6`. 11 | 12 | Notice that each operator includes one or more `@` symbols. An operand that is adjacent to this symbol is a [measure](./values.md#measures); otherwise, it is a number or integer. 13 | 14 | Here is an example of how the `@-@` and `@*` operators can be used to make a `div` element span the width of the viewport, but leaving a 32-pixel "margin" on each side: 15 | 16 | ```haskell 17 | module Example.StyleSheet where 18 | 19 | import Tecton 20 | import Tecton.Rule as Rule 21 | 22 | styleSheet :: CSS 23 | styleSheet = do 24 | body ? margin := nil 25 | div ? Rule.do 26 | margin := nil ~ auto -- horizontal centering 27 | width := vw 100 @-@ px 32 @* 2 28 | ``` 29 | 30 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNkl2j7EBEqk1dakSwAJWh4SLh22CZZQmIyCjgALhG4LJwcBn6ieKHKAF5pEAY4OAAjcAI4AH44YCUAc2YRUeWxKDXpZgA3Pe6OrDBV9fXD2hOzkYvmKDgAH4RCB4EBwAC04LgJHYzAAXnU8Ch-gURHg8qcjtd1lRmGA8CRznBblQ4ABGAAMFLgAAFwTS4OgkHAAMwAJlpACo4GymIdTqNxrYLFY4KJxAx+WdFtcYl4fH4AngsMFQti4Go4EUSjAyvlGG8NesAETadD0XTaeTgGC6Y1wPRwVV5UjkSjmiiEOCzQZuh26OCmgD01v4lqDHrtQA) -------------------------------------------------------------------------------- /docs/overview/rendering.md: -------------------------------------------------------------------------------- 1 | # Rendering 2 | 3 | Just as PureScript code must be compiled to JavaScript to run in the web browser, CSS rules written using Tecton must be rendered to plain CSS strings. Tecton provides two rendering functions depending on your use case, as well as options for pretty-printing or compact output. 4 | 5 | ## Inline styles 6 | 7 | To render a group of [declarations](./declarations.md), use the `renderInline` function. As the name suggests, the output is appropriate for inline styles, i.e. for use in the `style` attribute of an HTML element. For example: 8 | 9 | ```haskell 10 | module Example where 11 | 12 | import Prelude (Unit, ($), (<>)) 13 | import Effect (Effect) 14 | import TryPureScript (render) as TryPureScript 15 | import Unsafe.Coerce (unsafeCoerce) 16 | 17 | import Color (rgb) 18 | import Tecton 19 | import Tecton.Rule as Rule 20 | 21 | renderedInlineStyle :: String 22 | renderedInlineStyle = 23 | renderInline Rule.do 24 | width := px 400 25 | height := px 200 26 | backgroundColor := rgb 0 5 56 27 | color := rgb 232 199 148 28 | display := flex 29 | alignItems := center 30 | justifyContent := center 31 | fontFamily := sansSerif 32 | fontSize := px 32 33 | fontWeight := 700 34 | 35 | main :: Effect Unit 36 | main = 37 | TryPureScript.render 38 | $ unsafeCoerce 39 | $ "
renderedInlineStyle <> "\">Hello world!
" 40 | ``` 41 | 42 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdZwO4AsYBOMAUMQJYYgEAucACkVBGPABQCqAdmdQDRysAJAEp+rADwA+YcPKUaiAGaKYAY1qsEytdVkV0VWgBUCATzoQiAZVUEy6DUU4sCwuCgDOcE+cswbdg5yBgpcHigqAHQAwiCEqmwQnOEqsfEwssGGcLFQVAIEAOYARnryxjognFkKRpWckQBK0PCecM2wpE4uMGAAkpxQZJz+1KZYAFwTcFbUdpyFxN2EvQNDI7Pj8AC8xHBwywRrw-AdMJFgIHv7OGRg1LhwE9tw6EhwACwADF-X+-hkQq4WjPV7vABMPz+cGKKFUAGtCgQQEkwLl8qCisU4F84ABWfEANmhqhAeQITxeWLg4IAzOC4ABGACczKZHwAHNCwGQPJgUKZKXBFLAkNCUENCpw+tQYMAvKCEpxZQRoQArCAeahkRSmWLKmDKoVKlXQxRVagAMTQZCggtB4WSVkIOrNFqsZAAXvBQW84PS3cqAOowQHAoUAdihxGAKGGT2mWhU6jgXB4MbjnDgu32Pgs1ls9mokUO0MEcCSKRgaQICTLcAAROIeQA3OBarbbAA6DYbcCkB0NPX6gxOmywA4bPckAAkYFA8jgqFAwABCcQAelbkgbQA) 43 | 44 | ## Style sheets 45 | 46 | For a style sheet, which contains statements (e.g. [selectors](./selectors.md) and at-rules) instead of "top-level" declarations, use the `renderSheet` function. This function accepts a _configuration_ argument followed by the CSS parameter. For configuration, choose between the following options: 47 | 48 | 1. `pretty` prints the CSS output in a human-readable format. 49 | 2. `compact` optimizes the output for performance, for example removing unnecessary whitespace and using short hex strings for colors. 50 | 51 | The following example demonstrates how to render a style sheet with each configuration. Open it in Try PureScript to compare the output. 52 | 53 | ```haskell 54 | module Example where 55 | 56 | import Prelude (Unit, ($), (<>)) 57 | import Effect (Effect) 58 | import TryPureScript (render) as TryPureScript 59 | import Unsafe.Coerce (unsafeCoerce) 60 | 61 | import Color (rgb) 62 | import Tecton 63 | import Tecton.Rule as Rule 64 | 65 | containerClass = ClassName "container" :: ClassName 66 | 67 | css :: CSS 68 | css = 69 | universal &. containerClass ? Rule.do 70 | width := px 400 71 | height := px 200 72 | backgroundColor := rgb 0 5 56 73 | color := rgb 232 199 148 74 | display := flex 75 | alignItems := center 76 | justifyContent := center 77 | fontFamily := sansSerif 78 | fontSize := px 32 79 | fontWeight := 700 80 | 81 | main :: Effect Unit 82 | main = 83 | TryPureScript.render 84 | $ unsafeCoerce 85 | $ "" <> 86 | "
(\(ClassName c) -> c) containerClass <> "\">Hello world!
" <> 87 | "
\npretty:\n\n" <>
88 |       renderSheet pretty css <>
89 |       "\n\ncompact:\n" <>
90 |       renderSheet compact css <>
91 |       "
" 92 | ``` 93 | 94 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdZwO4AsYBOMAUMQJYYgEAucACkVBGPABQCqAdmdQDRysAJAEp+rADwA+YcPKUaiAGaKYAY1qsEytdVkV0VWgBUCATzoQiAZVUEy6DUU4sCwuCgDOcE+cswbdg5yBgpcHigqAHQAwiCEqmwQnOEqsfEwssGGcLFQVAIEAOYARnryxjognFkKRpWckQBK0PCecM2wpKpV1ChknITRUJ5eALw5wx4eAHJo8ABE3Zy9-YTzcABcGxMjs8AkxKpTm9vRVlaHx6PEcHBJZABuhOFQcABkkXBLKwMEQyNwAD87RakTAIButxwZDA1Fwm3G6CQcAALAAGNGQ274MiFXC0DaI5EAJgxWLgxRQqgA1oUCCAkmBcvlCXAisU4Gi4ABWHkANnJ3TyBARbJKcGJAGZiXAAIwATnlcpRAA5yWAyB5MChTKLFLAkOSUFBcZwAJLUGDALyshLLQjkgBWEA81DIilMsXty1FdstBHJih6ADE0GQoLrWeFklZCO7Az0rGQAF7wVlIuDShPLADqMFx+NFAHYycRgH1OCclCp1HAuDwyxW4Ndbj4LNZbPZqJEnC5yYI7skIjA0gQEv24PNxK7TLBJOspGyYM5CFZ8DBaN0MFTN8dF1OAPQzucLyTk25TjUPL6TDyjAA681PAnvrH+Uz28FUbgAtJIvm43wVoMt5wPuj6SAAEjAUB5DgVBQGAACE4gHle85gWeUJQlO6BEJI4jdCwkj3pweEbtQpgbKRpGnueS4rgQa4wBucDkdQlFfHuWHYZONGcFu6A7tRnB0bxDEuMxrGCTuXFeFI9GHuRBEHkRMDzkAA) -------------------------------------------------------------------------------- /docs/overview/syntax.md: -------------------------------------------------------------------------------- 1 | # Syntax 2 | 3 | ## Declarations 4 | 5 | The main building block of style sheets and inline styles alike is a declaration. A declaration consists of two parts: a property and a value. The property describes the aspect of the element you want to style, such as its font or color, and the value specifies what you want the property to be. Tecton uses the `:=` operator to set the property on the left-hand side to the value on the right. For example, here is how to set the color of the text content within an HTML element to black: 6 | 7 | ```haskell 8 | module Example.InlineStyle where 9 | 10 | import Color (black) 11 | import Tecton 12 | 13 | inlineStyle :: Declarations _ 14 | inlineStyle = color := black 15 | ``` 16 | 17 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhzhMqG55ACMoFBMAa1s9J2sQPHZiIhIKangALgG4ABFTeqYUckIO8IB9DgJiMioaAF44ExAqpjgBjbqGxvZgFGI94ctjMzhJaVZT87XWOAiXNw9Y3yx-QJfXuAKOB5AowIpMHL-V5AuAAIjU6BYGjUWxEGlhcE0cF+zHwPXg3WWfRoWPhAHpUTBkWTEVTYUA) 18 | 19 | ## Declaration blocks 20 | 21 | A simple rule can consist of a single declaration. However, in many cases you will need to use [qualified-do syntax](https://jordanmartinez.github.io/purescript-jordans-reference-site/content/11-Syntax/06-Modifying-Do-Ado-Syntax-Sugar/src/13-Qualified-Do-ps.html) to combine multiple declarations into a declaration block, as in the following: 22 | 23 | ```haskell 24 | module Example.InlineStyle where 25 | 26 | import Color (black, white) 27 | import Tecton 28 | import Tecton.Rule as Rule 29 | 30 | inlineStyle :: Declarations _ 31 | inlineStyle = Rule.do 32 | color := black 33 | backgroundColor := white 34 | ``` 35 | 36 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhzhMqG55ACMoFBMAazkGaRKy-UdrEDwOp268LAAlaHgwuBHYdmIiEgpqeAAuRbgAEVN6phRyQh7wgH0OAmIyKhoAXgnRrDAQVjg4ExAqpjhFy7qGxvu4Gq+AcyYIDyYEq1XedHobXYwBQxDeK0sxjMcEk0lYsPh5x+kTcHliviw-kCPweCjgeQKMCKTBypLg5LgACI1OgWBo1E8RBomXBNHBicx8LN4DMTvMaPyWQB6LkwDnStnyplAA) 37 | 38 | ## Rules 39 | 40 | A rule consists of a prelude and either nested rules or a declaration block, joined together using the `?` operator. 41 | 42 | > **Note** 43 | > The `?` operator is equivalent to the pair of curly braces surrounding a rule or declaration block in CSS. 44 | 45 | The most common type of rule is a style rule, consisting of a selector to the left of the `?` operator and a declaration block to the right, e.g. 46 | 47 | ```haskell 48 | module Example.StyleSheet where 49 | 50 | import Color (black, white) 51 | import Tecton 52 | import Tecton.Rule as Rule 53 | 54 | styleSheet :: CSS 55 | styleSheet = do 56 | universal ? Rule.do 57 | backgroundColor := black 58 | color := white 59 | ``` 60 | 61 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNklzgcqHY4AQAjKBR5AGspGnEYR2M3exARKpNXAZEsACVoeEi4KdgmWUJiMgo4AC51mpwcBiWieNXKAF5pEAY4OCLmADc89Kg4AH456awwc8vLxta2gHNaCAimBavV1qdmr8Ll95CA6rQNqcunhGAxgChmCINltbBYrHBROI0RiscdoTEvD4-AE8FhgqFoZc1FdihkYGV8owvnBmXAAETadD0XTaWH8XR8uB6OD0vKkciUIUUQhwfYrBVS3T87QAejFMBFOqVEqAA) 62 | 63 | Another type of rule is a media query, whose prelude specifies the conditions under which the nested rules apply. In the following example, the style rule applies to a printed page: 64 | 65 | ```haskell 66 | module Example.StyleSheet where 67 | 68 | import Tecton 69 | import Tecton.Rule as Rule 70 | 71 | styleSheet :: CSS 72 | styleSheet = do 73 | media print {} ? do 74 | body ? Rule.do 75 | margin := inch 1 76 | ``` 77 | 78 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNkl2j7EBEqk1dakSwAJWh4SLh22CZZQmIyCjgALhG4LJwcBn6ieKHKAF5pEAY4OGAYGRQ4dH8RSgBvAF84AH4VtfW4ACNwAnPujqwwVevr4CUAc2YRUeXfvISHAAIxMT6-UbjWwWKxwUTiBgQv6LK4xLw+PwBPBYYKhK7rNRwIolGBlfKMd5EuAAIm0exgum08nAjJpcD0cDxeVI5EoDLwhDgs0GfI5ulp2gA9Cz+EypQzdDSgA) 79 | 80 | ## Lists 81 | 82 | Lists can be found throughout CSS, notably in [selectors](./selectors.md) and many properties such as [`transitionProperty`](./animations.md#transitions) (which accepts a list of properties to animate). Tecton's list syntax uses [the `/\` operator (nested tuples)](https://pursuit.purescript.org/packages/purescript-tuples/4.0.0/docs/Data.Tuple.Nested#v:(/\\)). Here is how the syntax looks: 83 | 84 | ```haskell 85 | module Example.StyleSheet where 86 | 87 | import Data.Tuple.Nested ((/\)) 88 | import Tecton 89 | import Tecton.Rule as Rule 90 | 91 | styleSheet :: CSS 92 | styleSheet = do 93 | 94 | -- A selector list matching multiple HTML element types 95 | a /\ button /\ summary ? Rule.do 96 | 97 | -- A list of multiple properties to transition 98 | transitionProperty := color /\ backgroundColor 99 | 100 | -- A list of transition durations corresponding to each property 101 | transitionDuration := ms 150 /\ ms 75 102 | ``` 103 | 104 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoDKAXAT1hwAsYY84B3MgJxgCgGBLDEWygBXqgjHgAUAVQB2zPABo4YZgGcAxilpgpAgCQBKVQB4AfBo0s2HRADNTMeZQEJzlvIdbp2lACq0CnCPRzzazdGt6EX5aDTgUWTh3T28YX39Ao2cTUVkUCywAYRAYWnlBCBF0ixy8gsNklzgAERQ8FCxXCEwYLAA5GFk8GDA4AQEAegAdAyqTV3sQEXG3KZEsACVoeEi4ZdgmbqJ4sgo4AC4DuCycHAZt4j3KAF5pECY4OABaZ7gAQThZGFgrdjgoHJKMB6vISMwRABzODAaB4AKwOAACVcAFkADJwH4wYAwESUQjoLoMJ4oOAjOAAIwgeDw03Jwy+EGAII8cAA-OsVlgwA8SU8Xm9PoDunAQKYYXCEfB0LQQESOMwunA6SraChiuJmNN+U88OrNfDptx5XlCIc7vIQFB-hTKSh5ABrSFyopgHI22iPAWvD4AoFiiX6jWyLX0yDqo3FOBW2j0WTOEIQ6GqmAOkhwWWmjgEXVqkNhkQ1bz1bUiC0wqIARgArAAGBmVuAAdhrTBBEMOx1sFiscFE4gYHfLN35MS8Pj8ATwWGCoTzajgRRKMDK+UYArgi7gACJtLKYLptFb+Lod3A9HA53lSORKAfaQQvoQrneL7pd9pBifD1+D2egA) 105 | 106 | > **Note** 107 | > The `/\` operator is generally equivalent to `,` in CSS. 108 | 109 | > **Note** 110 | > If you're just getting started with Tecton, you may wonder why nested tuples are used to represent lists, rather than the more "obvious" structures like `Array` or even `List`. The reason for this relates to Tecton's highly polymorphic API, which is designed to mimic the "flexible" nature of CSS itself, whose design takes full advantage of dynamic typing. `Array` and `List` are homogenous structures, while list items in Tecton frequently have various data types. -------------------------------------------------------------------------------- /docs/overview/values.md: -------------------------------------------------------------------------------- 1 | # Values and units 2 | 3 | ## Component values 4 | 5 | Component values include keywords and various data types. A property value usually consists of a single component. In many cases, however, you may combine multiple components to form a complete property value. In CSS, these would typically be separated by spaces. With Tecton, you can use the `~` operator to join them together. For example: 6 | 7 | ```haskell 8 | module Example.InlineStyle where 9 | 10 | import Tecton 11 | import Tecton.Rule as Rule 12 | 13 | inlineStyle :: Declarations _ 14 | inlineStyle = Rule.do 15 | 16 | -- Setting top, x, and bottom padding in a single declaration 17 | padding := px 4 ~ px 8 ~ px 16 18 | 19 | -- Combining two keywords 20 | alignSelf := safe ~ center 21 | ``` 22 | 23 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhwjrEDwy-UdKvCwAJWh4MLhm2HZiIhIKangALgG4ABFTKBQmFHJCKvCAfQ4CYjIqGgBedpasMBB2ODgAWkO4UhhyGbwAczhyEHQ5JDkUALgAIxALkGA4dBQwMDEG7EEJwUJAmgiEwTKYzKqsA5-AFAuADTboJBwAAscAAfr9MQAOPEEuAARgAbPsjidMsA3sQUeRaCA4ABrGCUFlMMChBEhIhXPBnKAGVGbArwfE5PDkZjsYAoEFDQzGMxwSTSViKkHrfmRNweWK+LD+QL8g4KOB5SVFJg5C1wK1wABEanQLA0ahM4BgGhdcE0cDNzHwPXg3RWfRoQbdAHofSIvXGPX6XUA) 24 | 25 | ## CSS-wide keywords 26 | 27 | A few global keywords can be assigned to any property. In Tecton, these are 28 | * `initial`, which resets the property to its default value defined in the CSS specification; 29 | * `inherit`, which uses the property value inherited from the parent element; and 30 | * `unset`, which either inherits the value of an inheritable property or reverts to the initial value. 31 | 32 | Notice how `inherit`, for example, is compatible with each property: 33 | 34 | ```haskell 35 | module Example.InlineStyle where 36 | 37 | import Tecton 38 | import Tecton.Rule as Rule 39 | 40 | inlineStyle :: Declarations _ 41 | inlineStyle = Rule.do 42 | backgroundColor := inherit 43 | color := inherit 44 | fontSize := inherit 45 | margin := inherit 46 | outlineStyle := inherit 47 | ``` 48 | 49 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhwjrEDwy-UdKvCwAJWh4MLhm2HZiIhIKangALgG4ABFTKBQmFHJCKvCAfQ4CYjIqGgBedpasMBBWODgAIxQTAGsAcyYQPLBMqG44Ac3iRm94g5MQe6ZH57xX6T7OAGKrkUiEABegz+APecGAk3OxF+cBezEBB2u5B6q36KLRb3YCORQ0MxjMcEkgOJeDg6yBkTcHliviw-kCQIOCjgeQKMCKTBynLg3LgACI1OgWBo1J8RBoxXBNHB2cx8DjUctemt4MqJQB6OUwGX6qXGsVAA) 50 | 51 | ## URLs 52 | 53 | A `URL` is a pointer to a resource, such as an image, font, or custom cursor. To construct a `URL` value, simply apply the `url` function to a URL string. The following example uses the `url` function to create a reference to a background image: 54 | 55 | ```haskell 56 | module Example.InlineStyle where 57 | 58 | import Tecton 59 | 60 | inlineStyle :: Declarations _ 61 | inlineStyle = backgroundImage := url "./bg.gif" 62 | ``` 63 | 64 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhwjrEDx2YiISCmp4AC4muAARUygUJhRyQirwgH0OAmIyKhoAXjgAIxQTAGsAcyYQPLAcYBQl5un3KDgAIiwAehmlrCXCA0P2LeI4FsNjMzhJaVZ7vDhJ1jgIlxuDyxXxYfyBP7-OAKOB5AowIpMHKQ-4wo5qdAsDRqEzgGAaQ5wTRwcHMfB1eC1MYNGjEw5qE64kTYk6Y-GHIA) 65 | 66 | ## Measures 67 | 68 | The `Measure` type provides a common interface for many types of values, such as lengths, angles, and durations, allowing them to be calculated and/or combined via [calc expressions](./calc.md). It is a [phantom type](https://jordanmartinez.github.io/purescript-jordans-reference-site/content/31-Design-Patterns/03-Phantom-Types/01-What-Are-Phantom-Types.html), meaning that each value is tagged as a `Length`, `Percentage`, `Time`, etc. This tag ensures that incompatible values can't be combined (such as a `Measure Time` with a `Measure Length`). It also prevents the wrong kind of value from being assigned to a given property, e.g. a percentage value to a property that only supports a length. 69 | 70 | You can construct a measure by applying the appropriate function (named for the corresponding CSS unit), followed by a number or integer, e.g. `px 100` (rendered as `100px`). The following table lists the various types of measures along with the functions that can be used to construct their values. 71 | 72 | | Measure type | Constructors | 73 | | ------------ | ------------ | 74 | | `Length` | `ch`, `em`, `ex`, `rem`, `vh`, `vw`, `vmin`, `vmax`, `px`, `cm`, `mm`, `pc`, `pt`, `inch` | 75 | | `Percentage` | `pct` | 76 | | `Angle` | `deg`, `rad`, `turn` | 77 | | `Time` | `ms`, `sec` | 78 | | `LengthPercentage` | via [calc expressions](./calc.md) | 79 | | `Nil` | `nil` | 80 | 81 | In some cases, measures of different types can be used interchangeably. For example, `nil` produces a value that can be used as a length, percentage, angle, or time. Similarly, values of type `Measure Length` or `Measure Percentage` can be used wherever a `Measure LengthPercentage` is expected (although the reverse is not true). 82 | 83 | ## Colors 84 | 85 | 86 | 87 | Tecton is compatible with the [`Color` type from the _colors_ package](https://pursuit.purescript.org/packages/purescript-colors/7.0.1/docs/Color#t:Color), along with the following CSS-specific color keywords: 88 | * `transparent` 89 | * `currentColor` 90 | 91 | ## Images 92 | 93 | 94 | 95 | Images can be used for backgrounds, custom list markers, and more. An image consists of either a [`URL` value](#urls) or a [gradient](#gradients). 96 | 97 | ### Gradients 98 | 99 | #### Linear gradients 100 | 101 | A linear gradient can be constructed using the `linearGradient` function, parameterized by an [angle](#measures) and a [color stop list](#color-stop-lists). 102 | 103 | For example, the following declaration creates a background image that transitions top to bottom from black to white: 104 | 105 | ```haskell 106 | module Example.InlineStyle where 107 | 108 | import Color (black, white) 109 | import Data.Tuple.Nested ((/\)) 110 | import Tecton 111 | 112 | inlineStyle :: Declarations _ 113 | inlineStyle = backgroundImage := linearGradient (deg 180) (black /\ white) 114 | ``` 115 | 116 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhzhMqG55ACMoFBMAazkGaRKy-QARFHIULEcITBgsADkYUPIYMHlRAHoAHW0Op2sQPHZiIhIKangALj24TtN6ph7CNfCAfQ4CYjIqGgBeOBqGxoBzJhA8sBxgFAffYvLYwFBMADiZzAhBgeHMIg+cAAjAAOAAMwVEdXecAWdHobVKAOIcAOhmMZjgkmkrBJeDgT1YcAiLjcHliviw-kCzJZcAUcDyBRgRSYOT5LMFcAARGp0CwNGoTOAYBoZXBNHAecx8KC4Jt7jsaFq5bMVSIlbMFWqZUA) 117 | 118 | #### Radial gradients 119 | 120 | You can create a radial gradient using the `radialGradient` function. 121 | 122 | The first parameter accepts any of the following: 123 | * A shape and extent (e.g. `circle ~ closestSide` or `ellipse ~ farthestCorner`) 124 | * A length (e.g. `px 100`), representing a circle radius 125 | * A pair of length-percentage values, representing the horizontal and vertical radii of an ellipse 126 | 127 | The second parameter accepts the [position](#positions) of the center point of the gradient. 128 | 129 | The third parameter accepts a [color stop list](#color-stop-lists). 130 | 131 | #### Color stop lists 132 | 133 | A color stop [list](#lists) defines the colors and their respective positions within a gradient. Each color stop consists of a color and an optional length-percentage position, e.g. `rgb 255 0 0` or `black ~ pct 50`. A length-percentage transition hint may optionally be inserted between two color stops to set the midpoint in the color transition. 134 | 135 | The following gradient demonstrates all of these options: 136 | 137 | ```haskell 138 | module Example.InlineStyle where 139 | 140 | import Color (rgb) 141 | import Data.Tuple.Nested ((/\)) 142 | import Tecton 143 | 144 | inlineStyle :: Declarations _ 145 | inlineStyle = 146 | backgroundImage := 147 | linearGradient nil $ 148 | rgb 0 160 0 /\ rgb 0 100 0 ~ pct 75 /\ pct 80 /\ rgb 135 206 235 149 | ``` 150 | 151 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhzhMqG55JgBzACNbPV4AERRyFCxHCEwYLAA5GFDyGDB5UQB6AB1tMv1HaxA8dmIiEgpqeAAuLbgW0ygUJnbCJfCAfQ4CYjIqGgBeVjg4epQTAGtaphA8sBxgFC1baPZ7PNYwI4AcWOYEIMDwvCkUDgCieoOedXqcAADHAAIwANlxuOmcExOPx2OJcAAfnB0GY4AB2ACscFJDN4AA4SVMyQ18QBmNkAJmxBLgIuF7ABxDgO0MxkZkmkrFleDgIIiLjcHliviw-kCaOeCjgeQKMCKTByJpRzwARGp0CwNGoTOAYBoHXBNGT4YF8OC4KsbhsaH6nRMPSI3RMXV6HUA) 152 | 153 | #### Repeating gradients 154 | 155 | To create a repeating linear or radial gradient, simply apply the `repeating` function to the gradient, e.g. 156 | 157 | ```haskell 158 | module Example.InlineStyle where 159 | 160 | import Color (black, white) 161 | import Data.Tuple.Nested ((/\)) 162 | import Tecton 163 | 164 | inlineStyle :: Declarations _ 165 | inlineStyle = backgroundImage := repeating $ linearGradient nil $ black /\ white 166 | ``` 167 | 168 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=LYewJgrgNgpgBAUQB4ENgAdYDoCSA7KASzxgGUAXAT1jgHcALGAJxgChXCMQny4AFFlAhh4ACgCqeQuQA0cUQBIAlHNEAeAHxKlHLj0QAzAzADGvUQiOnyOzum68AKk0p8ILUiaaF05lnhEmJTgUAGc4Z1d3Mi8fcl17fUlQlGMsAGEQZhMxCDwU40zsmB0EhzhMqG55ACMoFBMAazkGaRKy-QARFHIULEcITBgsADkYUPIYMHlRAHoAHW0Op2sQPHZiIhIKangALj24TtN6ph7CNfCAfQ4CYjIqGgBeOBqGxoBzJhA8sBxgFAffYvFjoGDnPAfOAKOBbcFMADiZzAhBgeF4Uig0Ne9SacAWdHobXYAOIcAOhmMZjgkmkrFJeDgT1YcAiLjcHliviw-kCLNZ2LyBRgRSYOX5rJhcAARGp0CwNGoTOAYBppXBNHBecx8HC4Jt7jsaJrZbNlSJFbN5arpUA) 169 | 170 | ## Positions 171 | 172 | 173 | 174 | Position values are used in background positioning, transforms, and gradients. A position consists of one of the following: 175 | 176 | * `center` 177 | * `top` 178 | * `right` 179 | * `bottom` 180 | * `left` 181 | * X/Y keyword positions, e.g. `left ~ top` 182 | * single length-percentage value, e.g. `px 25` or `pct 50` 183 | * X/Y length-percentage values, e.g. `pct 10 ~ px 25` -------------------------------------------------------------------------------- /example.dhall: -------------------------------------------------------------------------------- 1 | let conf = ./spago.dhall 2 | 3 | in conf // { 4 | dependencies = conf.dependencies # ["console", "effect"] 5 | } 6 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | You can use the following command to run an example, replacing `` with the 4 | appropriate value: 5 | ```bash 6 | spago -x example.dhall run -p examples/.purs -m Example. 7 | ``` 8 | 9 | ## CSS output 10 | The [`output`](./output) subdirectory contains the CSS output produced by each example. 11 | 12 | ## Type errors 13 | The [`type-errors`](./type-errors) subdirectory contains examples of type safety that Tecton adds to CSS. All of these examples fail to compile (intentionally). 14 | -------------------------------------------------------------------------------- /examples/type-errors/CursorMissingFallback.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because the value of the `cursor` property as 4 | defined in the Basic User Interface Module Level 4 specification requires the 5 | last entry to be a generic/native cursor rather than an image URL. 6 | 7 | See https://www.w3.org/TR/css-ui-4/#propdef-cursor for more information. 8 | 9 | -} 10 | 11 | module TypeError.CursorMissingFallback where 12 | 13 | import Data.Tuple.Nested ((/\)) 14 | import Tecton (CSS, cursor, universal, url, (:=), (?)) 15 | 16 | css :: CSS 17 | css = universal ? cursor := url "abc.png" /\ url "xyz.png" -------------------------------------------------------------------------------- /examples/type-errors/FontFaceMissingFontFamily.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because a `@font-face` rule must include a 4 | `font-family` descriptor. 5 | 6 | See https://www.w3.org/TR/css-fonts-4/#font-family-desc for more information. 7 | 8 | -} 9 | 10 | module TypeError.FontFaceMissingFontFamily where 11 | 12 | import Tecton (CSS, fontFace, fontFamily, src, url, (:=), (?)) 13 | import Tecton.Rule as Rule 14 | 15 | css :: CSS 16 | css = 17 | fontFace ? Rule.do 18 | src := url "foo.woff" 19 | -------------------------------------------------------------------------------- /examples/type-errors/FontFaceMissingSrc.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because a `@font-face` rule must include a `src` 4 | descriptor. 5 | 6 | See https://www.w3.org/TR/css-fonts-4/#src-desc for more information. 7 | 8 | -} 9 | 10 | module TypeError.FontFaceMissingSrc where 11 | 12 | import Tecton (CSS, fontFace, fontFamily, (:=), (?)) 13 | 14 | css :: CSS 15 | css = fontFace ? fontFamily := "Roboto" 16 | -------------------------------------------------------------------------------- /examples/type-errors/FontFamilyMissingFallback.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because the Fonts Module Level 4 specification 4 | encourages authors "to append a generic font family as a last alternative for 5 | improved robustness" within `font-family` declarations. 6 | 7 | See https://www.w3.org/TR/css-fonts-4/#generic-family-value for more 8 | information. 9 | 10 | -} 11 | 12 | module TypeError.FontFamilyMissingFallback where 13 | 14 | import Data.Tuple.Nested ((/\)) 15 | import Tecton (CSS, fontFamily, universal, (:=), (?)) 16 | 17 | css :: CSS 18 | css = universal ? fontFamily := "Roboto" /\ "Arial" 19 | -------------------------------------------------------------------------------- /examples/type-errors/GradientConsecutiveTransitionHints.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because the gradient `` syntax 4 | does not allow two consecutive transition hints. 5 | 6 | See https://www.w3.org/TR/css-images-3/#typedef-color-stop-list for more 7 | information. 8 | 9 | -} 10 | 11 | module TypeError.GradientConsecutiveTransitionHints where 12 | 13 | import Prelude 14 | 15 | import Color (black, white) 16 | import Data.Tuple.Nested ((/\)) 17 | import Tecton 18 | ( CSS 19 | , backgroundImage 20 | , deg 21 | , linearGradient 22 | , pct 23 | , universal 24 | , (:=) 25 | , (?) 26 | ) 27 | import Tecton.Rule as Rule 28 | 29 | css :: CSS 30 | css = 31 | universal ? Rule.do 32 | backgroundImage := 33 | linearGradient (deg 180) $ black /\ pct 40 /\ pct 50 /\ white 34 | -------------------------------------------------------------------------------- /examples/type-errors/GradientInsufficientColorStops.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because the gradient `` syntax 4 | requires at minimum two color stops. 5 | 6 | See https://www.w3.org/TR/css-images-3/#typedef-color-stop-list for more 7 | information. 8 | 9 | -} 10 | 11 | module TypeError.GradientInsufficientColorStops where 12 | 13 | import Color (black, white) 14 | import Tecton (CSS, backgroundImage, deg, linearGradient, universal, (:=), (?)) 15 | import Tecton.Rule as Rule 16 | 17 | css :: CSS 18 | css = 19 | universal ? Rule.do 20 | backgroundImage := linearGradient (deg 180) black 21 | -------------------------------------------------------------------------------- /examples/type-errors/GridTemplateColumnsAutoRepeatWithFlex.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because `` values (e.g. `1fr`) cannot appear 4 | in the same track list as `` values (e.g. 5 | `repeat(auto-fill, 100px 10%)`). 6 | 7 | See https://www.w3.org/TR/css-grid-1/#typedef-auto-track-list for more 8 | information. 9 | 10 | -} 11 | 12 | module TypeError.GridTemplateColumnsAutoRepeatWithFlex where 13 | 14 | import Data.Tuple.Nested ((/\)) 15 | import Tecton 16 | ( CSS 17 | , autoFill 18 | , fr 19 | , gridTemplateColumns 20 | , pct 21 | , px 22 | , repeat 23 | , universal 24 | , (:=) 25 | , (?) 26 | ) 27 | import Tecton.Rule as Rule 28 | 29 | css :: CSS 30 | css = 31 | universal ? Rule.do 32 | gridTemplateColumns := fr 1 /\ repeat autoFill (px 100 /\ pct 10) 33 | -------------------------------------------------------------------------------- /examples/type-errors/GridTemplateColumnsMultipleAutoRepeat.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because multiple `` values (e.g. 4 | `repeat(auto-fit, 100px)` or `repeat(auto-fill, 10%)`) cannot appear within the 5 | same track list. 6 | 7 | See https://www.w3.org/TR/css-grid-1/#typedef-auto-track-list for more 8 | information. 9 | 10 | -} 11 | 12 | module TypeError.GridTemplateColumnsMultipleAutoRepeat where 13 | 14 | import Data.Tuple.Nested ((/\)) 15 | import Tecton 16 | ( CSS 17 | , autoFill 18 | , autoFit 19 | , gridTemplateColumns 20 | , pct 21 | , px 22 | , repeat 23 | , universal 24 | , (:=) 25 | , (?) 26 | ) 27 | import Tecton.Rule as Rule 28 | 29 | css :: CSS 30 | css = 31 | universal ? Rule.do 32 | gridTemplateColumns := repeat autoFit (px 100) /\ repeat autoFill (pct 10) 33 | -------------------------------------------------------------------------------- /examples/type-errors/KeyframesNonAnimatableProperty.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because only certain properties are animatable 4 | and therefore compatible with `@keyframes` animations; and `content` is not 5 | among them. 6 | 7 | See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties 8 | for more information. 9 | 10 | -} 11 | 12 | module TypeError.KeyframesNonAnimatableProperty where 13 | 14 | import Prelude 15 | 16 | import Tecton 17 | ( CSS 18 | , KeyframesName(..) 19 | , content 20 | , keyframes 21 | , nil 22 | , pct 23 | , (:=) 24 | , (?) 25 | ) 26 | import Tecton.Rule as Rule 27 | 28 | css :: CSS 29 | css = do 30 | keyframes (KeyframesName "foo") ? do 31 | pct 0 ? Rule.do 32 | content := "" 33 | pct 100 ? Rule.do 34 | content := "hello" 35 | -------------------------------------------------------------------------------- /examples/type-errors/README.md: -------------------------------------------------------------------------------- 1 | # Type error examples 2 | 3 | The examples in this directory demonstrate the type safety Tecton adds to CSS. Therefore, each one fails to compile. 4 | 5 | You can attempt to compile an example by running: 6 | 7 | ```bash 8 | spago build -p examples/type-errors/.purs 9 | ``` 10 | -------------------------------------------------------------------------------- /examples/type-errors/RulesetPropertyAppearsTwice.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because the same property appears twice within the 4 | same ruleset. Although in some cases this type of issue is harmless and the last 5 | declaration simply overrides previous ones, it is unintentional and should be 6 | reviewed. 7 | 8 | -} 9 | 10 | module TypeError.RulesetPropertyAppearsTwice where 11 | 12 | import Color (black, white) 13 | import Tecton (CSS, color, universal, (:=), (?)) 14 | import Tecton.Rule as Rule 15 | 16 | css :: CSS 17 | css = 18 | universal ? Rule.do 19 | color := white 20 | color := black 21 | -------------------------------------------------------------------------------- /examples/type-errors/SelectorMultiplePseudoElements.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because multiple pseudo-elements within a selector 4 | would most likely be unintentional and represent a defect. 5 | 6 | However, the Selectors Level 4 specification does include the notion of 7 | "sub-pseudo-elements" here: 8 | https://www.w3.org/TR/selectors-4/#sub-pseudo-elements 9 | 10 | For now, real-world use cases and browser support for "sub-pseudo-elements" are 11 | unclear. Google it: https://www.google.com/search?q=%22sub+pseudo+elements%22 12 | 13 | -} 14 | 15 | module TypeError.SelectorMultiplePseudoElements where 16 | 17 | import Tecton 18 | ( CSS 19 | , after 20 | , color 21 | , currentColor 22 | , placeholder 23 | , universal 24 | , (&::) 25 | , (:=) 26 | , (?) 27 | ) 28 | import Tecton.Rule as Rule 29 | 30 | css :: CSS 31 | css = 32 | universal &:: placeholder &:: after ? Rule.do 33 | color := currentColor 34 | -------------------------------------------------------------------------------- /examples/type-errors/SelectorPseudoClassingPseudoElement.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because a pseudo-class cannot follow a 4 | pseudo-element; therefore, the use of `:hover` is invalid. 5 | 6 | This is contrary to the Selectors Level 4 specification 7 | (https://www.w3.org/TR/selectors-4/#pseudo-element-states) and should be 8 | reviewed in the future. However, as of now, neither Safari 14.1, nor Chrome 107, 9 | nor Firefox 107, supports this notion of "pseudo-classing pseudo-elements". 10 | 11 | -} 12 | 13 | module TypeError.SelectorPseudoClassingPseudoElement where 14 | 15 | import Tecton 16 | ( CSS 17 | , after 18 | , hover 19 | , textDecorationLine 20 | , underline 21 | , universal 22 | , (&:) 23 | , (&::) 24 | , (:=) 25 | , (?) 26 | ) 27 | import Tecton.Rule as Rule 28 | 29 | css :: CSS 30 | css = 31 | universal &:: after &: hover ? Rule.do 32 | textDecorationLine := underline 33 | -------------------------------------------------------------------------------- /examples/type-errors/SelectorPseudoElementDescendant.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example does not compile because pseudo-elements do not exist in the DOM 4 | tree and therefore cannot have descendants. 5 | 6 | See https://www.w3.org/TR/selectors-4/#pseudo-element-attachment for more 7 | information. 8 | 9 | -} 10 | 11 | module TypeError.SelectorPseudoElementDescendant where 12 | 13 | import Tecton (CSS, after, nil, universal, width, (&::), (:=), (?), (|*)) 14 | import Tecton.Rule as Rule 15 | 16 | css :: CSS 17 | css = 18 | universal &:: after |* universal ? Rule.do 19 | width := nil 20 | -------------------------------------------------------------------------------- /examples/type-errors/TransitionPropertyNotAnimatable.purs: -------------------------------------------------------------------------------- 1 | {- 2 | 3 | This example fails to compile because only certain properties are animatable 4 | and therefore compatible with `transition-property`; and `align-content` is not 5 | among them. 6 | 7 | See https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties 8 | for more information. 9 | 10 | -} 11 | 12 | module TypeError.TransitionPropertyNotAnimatable where 13 | 14 | import Tecton (CSS, alignContent, transitionProperty, universal, (:=), (?)) 15 | import Tecton.Rule as Rule 16 | 17 | css :: CSS 18 | css = 19 | universal ? Rule.do 20 | transitionProperty := alignContent 21 | -------------------------------------------------------------------------------- /meta/test-count.json: -------------------------------------------------------------------------------- 1 | {"schemaVersion":1,"label":"tests","message":"1759","color":"cd523e"} 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "check-docs": "node ./scripts/docs-tryps.mjs --check", 4 | "check-examples": "node ./scripts/check-examples.mjs", 5 | "dev-docs": "parallel --will-cite ::: \"watchexec -w docs --exts md node ./scripts/docs-tryps.mjs\" \"mdbook serve\"" 6 | }, 7 | "devDependencies": { 8 | "chalk": "^5.1.2", 9 | "diff": "^5.1.0", 10 | "execa": "^6.1.0", 11 | "globby": "^13.1.3", 12 | "lz-string": "^1.5.0" 13 | } 14 | } -------------------------------------------------------------------------------- /packages.dhall: -------------------------------------------------------------------------------- 1 | let upstream = 2 | https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221015/packages.dhall 3 | sha256:4949f9f5c3626ad6a83ea6b8615999043361f50905f736bc4b7795cba6251927 4 | 5 | in upstream 6 | -------------------------------------------------------------------------------- /scripts/check-examples.mjs: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import fs from "fs/promises"; 3 | import path from "path"; 4 | import { execa } from "execa"; 5 | import { diffLines } from "diff"; 6 | 7 | for (let x of ["examples", "example.dhall"]) { 8 | try { 9 | await fs.access(x); 10 | } 11 | catch { 12 | throw new Error(`Missing "${x}". Run this via "npm run check-examples".`); 13 | } 14 | } 15 | 16 | const updateFlag = process.argv.includes("--update"); 17 | 18 | const examplesDir = "examples"; 19 | const outputDir = path.join(examplesDir, "output"); 20 | 21 | await fs.mkdir(outputDir, { recursive: true }); 22 | 23 | const examplesListing = await fs.readdir(examplesDir); 24 | const examples = examplesListing.flatMap(x => x.match(/^[A-Za-z]+(?=\.purs$)/g) || []); 25 | 26 | for (let example of examples) { 27 | const exampleFile = path.join(outputDir, `${example}.css`); 28 | 29 | let expected = null; 30 | try { 31 | expected = await fs.readFile(exampleFile, "utf8"); 32 | } 33 | catch {} 34 | 35 | const { stdout: actual } = await execa("spago", ["-x", "example.dhall", "run", "-p", path.join(examplesDir, `${example}.purs`), "-m", `Example.${example}`]); 36 | 37 | const needsUpdate = updateFlag || !expected; 38 | 39 | if (needsUpdate) { 40 | await fs.writeFile(exampleFile, actual); 41 | } 42 | else { 43 | const diffs = diffLines(expected, actual); 44 | if (diffs.some(({ added, removed }) => added || removed)) { 45 | process.stderr.write(`Unexpected changes in ${example}.css:\n\n`); 46 | for (let part of diffs) { 47 | const color = part.added ? "green" : part.removed ? "red" : "grey"; 48 | if (part.added) { 49 | process.stderr.write(chalk.green(part.value)); 50 | } 51 | else if (part.removed) { 52 | process.stderr.write(chalk.red(part.value)); 53 | } 54 | else { 55 | process.stderr.write(chalk.gray(part.value)); 56 | } 57 | } 58 | process.exit(1); 59 | } 60 | } 61 | } 62 | 63 | process.stdout.write(chalk.green("✓ CSS output is unchanged.") + "\n"); 64 | 65 | const typeErrorsDir = path.join(examplesDir, "type-errors"); 66 | 67 | const typeErrorsListing = await fs.readdir(typeErrorsDir); 68 | const typeErrors = typeErrorsListing.flatMap(x => x.match(/^[A-Za-z]+(?=\.purs$)/g) || []); 69 | 70 | for (let typeError of typeErrors) { 71 | try { 72 | await execa("spago", ["build", "-p", path.join(typeErrorsDir, `${typeError}.purs`)]); 73 | process.stdout.write(chalk.red(`✗ TypeError.${typeError} compiled successfully.\n`)); 74 | process.exit(1); 75 | } 76 | catch { 77 | continue; 78 | } 79 | } 80 | 81 | process.stdout.write(chalk.green("✓ All type error examples fail to compile.") + "\n"); 82 | -------------------------------------------------------------------------------- /scripts/docs-tryps.mjs: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | import path from "path"; 3 | import { fileURLToPath } from "url"; 4 | import { globby } from "globby"; 5 | import lz from "lz-string"; 6 | import chalk from "chalk"; 7 | 8 | let 9 | paths = [], 10 | exitCode = 0; 11 | 12 | if (process.env.WATCHEXEC_WRITTEN_PATH) { 13 | paths = process.env.WATCHEXEC_WRITTEN_PATH.split(path.delimiter); 14 | } 15 | else { 16 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 17 | paths = await globby(path.join(__dirname, "..", "docs", "**", "*.md")); 18 | } 19 | 20 | for (let p of paths) { 21 | const original = await fs.readFile(p, "utf8"); 22 | const updated = updateMarkdown(original); 23 | if (original !== updated) { 24 | if (process.argv.includes("--check")) { 25 | process.stdout.write(chalk.red(`Try PureScript link is missing or stale in ${p}`)); 26 | exitCode = 1; 27 | } 28 | else { 29 | process.stdout.write(`* Updating ${p}…`); 30 | await fs.writeFile(p, updated); 31 | process.stdout.write("done!\n"); 32 | } 33 | } 34 | } 35 | 36 | if (exitCode) { 37 | process.exit(exitCode); 38 | } 39 | 40 | function updateMarkdown(markdown) { 41 | return markdown.replace(/```haskell[\S\s]+?```(\s*\[!\[Open with Try PureScript\S+)?/gm, content => { 42 | let lines = content.trim().split("\n"); 43 | lines = lines.slice(lines.findIndex(x => /```haskell/.test(x)) + 1); 44 | lines = lines.slice(0, lines.findIndex(x => /```/.test(x))); 45 | const code = lines.join("\n"); 46 | return `\`\`\`haskell 47 | ${code} 48 | \`\`\` 49 | 50 | [![Open with Try PureScript](https://shields.io/badge/-Open%20in%20Try%20PureScript-303748?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAAA0VXHyAAAArElEQVQ4EeWRzQ6CMBCECSJnwOfiwN2YmCgn49F49cnrzNLdDFhfQDdpd+fbH2hbVf9lKaUaizZ/PTmSByv53I5AO8FjcQgKWqyXFGrYs0nAFEMAn0wEKARIN45ZSzMNfza1bHu4u2gNOzYIuFDMDnKyc73xN2gOdzv51w2YWKbHbzpTn7sfwQAmH0mIuHe98bzYNho1QGKUYr41n6xkg/atYlRfc0e9Svy+eAM93kRyOW/z2AAAAABJRU5ErkJggg==&style=flat)](https://try.purescript.org/?code=${lz.compressToEncodedURIComponent(tryPSCode(code))})`; 51 | }); 52 | } 53 | 54 | function tryPSCode(code) { 55 | const moduleName = (code.trim().match(/^module (\S+)/) || [])[1]; 56 | switch (moduleName) { 57 | case "Example.StyleSheet": 58 | return `module Example.StyleSheet where 59 | 60 | import Prelude (Unit, discard, ($), (<>)) 61 | import Effect (Effect) 62 | import TryPureScript (render) as TryPureScript 63 | import Unsafe.Coerce (unsafeCoerce) 64 | 65 | ${removeModuleDeclaration(code)} 66 | 67 | main :: Effect Unit 68 | main = 69 | TryPureScript.render 70 | $ unsafeCoerce 71 | $ "
" <> renderSheet pretty styleSheet <> "
"`; 72 | case "Example.InlineStyle": 73 | return `module Example.InlineStyle where 74 | 75 | import Prelude (Unit, ($), (<>)) 76 | import Effect (Effect) 77 | import TryPureScript (render) as TryPureScript 78 | import Unsafe.Coerce (unsafeCoerce) 79 | 80 | ${removeModuleDeclaration(code)} 81 | 82 | main :: Effect Unit 83 | main = 84 | TryPureScript.render 85 | $ unsafeCoerce 86 | $ "
" <> renderInline inlineStyle <> "
"`; 87 | default: 88 | return code; 89 | } 90 | 91 | function removeModuleDeclaration(code) { 92 | const lines = code.split("\n"); 93 | const ix = lines.findIndex(x => x.trim().endsWith("where")); 94 | if (ix < 0) return code; 95 | lines.splice(0, ix + 1); 96 | while (lines.length && !lines[0].trim()) lines.splice(0, 1); 97 | return lines.join("\n"); 98 | } 99 | } -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import (builtins.fetchTarball { 3 | url = "https://github.com/NixOS/nixpkgs/archive/21.11.tar.gz"; 4 | }) {}; 5 | 6 | # To update to a newer version of easy-purescript-nix: 7 | # 1. Obtain the commit hash via `curl https://api.github.com/repos/justinwoo/easy-purescript-nix/commits/master`. 8 | # 2. Obtain the sha256 hash via `nix-prefetch-url --unpack https://github.com/justinwoo/easy-purescript-nix/archive/.tar.gz`. 9 | # 3. Update the and below. 10 | pursPkgs = import (pkgs.fetchFromGitHub { 11 | owner = "justinwoo"; 12 | repo = "easy-purescript-nix"; 13 | rev = "ee51a6d459b8fecfcb10f24ca9728e649c6a9e00"; 14 | sha256 = "1w4k6mcayyg6388na0cca5qx94sm99xn3na26x2w34jlyz3bwl3m"; 15 | }) {inherit pkgs;}; 16 | in 17 | pkgs.stdenv.mkDerivation { 18 | name = "tecton"; 19 | buildInputs = with pursPkgs; 20 | [ 21 | purs 22 | spago 23 | pulp 24 | purs-tidy 25 | ] 26 | ++ (with pkgs; [ 27 | mdbook 28 | parallel 29 | watchexec 30 | nodejs-16_x 31 | nodePackages.bower 32 | ]); 33 | } 34 | -------------------------------------------------------------------------------- /spago.dhall: -------------------------------------------------------------------------------- 1 | { name = "tecton" 2 | , license = "MIT" 3 | , repository = "https://github.com/nsaunders/purescript-tecton" 4 | , dependencies = 5 | [ "colors" 6 | , "either" 7 | , "foldable-traversable" 8 | , "integers" 9 | , "lists" 10 | , "numbers" 11 | , "prelude" 12 | , "record" 13 | , "strings" 14 | , "transformers" 15 | , "tuples" 16 | , "web-html" 17 | ] 18 | , packages = ./packages.dhall 19 | , sources = [ "src/**/*.purs" ] 20 | } 21 | -------------------------------------------------------------------------------- /src/Tecton/Internal.js: -------------------------------------------------------------------------------- 1 | export function quote(s) { 2 | return JSON.stringify(s); 3 | } 4 | -------------------------------------------------------------------------------- /src/Tecton/Rule.purs: -------------------------------------------------------------------------------- 1 | module Tecton.Rule where 2 | 3 | import Prelude hiding (discard) 4 | 5 | import Control.Monad.Writer (Writer) 6 | import Prim.Row as Row 7 | import Type.Proxy (Proxy(..)) 8 | 9 | discard 10 | :: forall w a b c 11 | . Monoid w 12 | => Row.Union a b c 13 | => Row.Nub c c 14 | => Writer w (Proxy a) 15 | -> (Proxy a -> Writer w (Proxy b)) 16 | -> Writer w (Proxy c) 17 | discard wa awb = wa >>= \a -> awb a >>= \_ -> pure Proxy 18 | -------------------------------------------------------------------------------- /test.dhall: -------------------------------------------------------------------------------- 1 | let conf = ./spago.dhall 2 | 3 | in conf // { 4 | sources = conf.sources # ["test/**/*.purs"], 5 | dependencies = conf.dependencies # ["aff", "effect", "spec"] 6 | } 7 | -------------------------------------------------------------------------------- /test/AnimationsSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-animations-1/ 2 | 3 | module Test.AnimationsSpec where 4 | 5 | import Prelude 6 | 7 | import Data.Tuple.Nested ((/\)) 8 | import Tecton 9 | ( KeyframesName(..) 10 | , all 11 | , alternate 12 | , alternateReverse 13 | , animationDelay 14 | , animationDirection 15 | , animationDuration 16 | , animationFillMode 17 | , animationIterationCount 18 | , animationName 19 | , animationPlayState 20 | , animationTimingFunction 21 | , backwards 22 | , both 23 | , cubicBezier 24 | , ease 25 | , easeIn 26 | , easeInOut 27 | , easeOut 28 | , end 29 | , forwards 30 | , infinite 31 | , inherit 32 | , initial 33 | , jumpBoth 34 | , jumpEnd 35 | , jumpNone 36 | , jumpStart 37 | , keyframes 38 | , linear 39 | , media 40 | , ms 41 | , nil 42 | , none 43 | , normal 44 | , paused 45 | , pct 46 | , px 47 | , reverse 48 | , running 49 | , sec 50 | , start 51 | , stepEnd 52 | , stepStart 53 | , steps 54 | , unset 55 | , width 56 | , (:=) 57 | , (?) 58 | , (@*) 59 | , (@+@) 60 | , (@/) 61 | ) 62 | import Test.Spec (Spec, describe) 63 | import Test.Util (isRenderedFromInline, isRenderedFromSheet) 64 | 65 | spec :: Spec Unit 66 | spec = 67 | describe "Animations Module" do 68 | 69 | describe "Keyframes" do 70 | 71 | let isRenderedFrom = isRenderedFromSheet 72 | 73 | "@keyframes foo{0%{width:0}100%{width:500px}}" 74 | `isRenderedFrom` do 75 | keyframes (KeyframesName "foo") ? do 76 | pct 0 ? width := nil 77 | pct 100 ? width := px 500 78 | 79 | "@media all{@keyframes foo{0%,100%{width:0}}}" 80 | `isRenderedFrom` do 81 | media all {} ? do 82 | keyframes (KeyframesName "foo") ? do 83 | pct 0 /\ pct 100 ? width := nil 84 | 85 | "@keyframes foo{0%{width:75%}20%{width:80%}50%{width:100%}}" 86 | `isRenderedFrom` do 87 | keyframes (KeyframesName "foo") ? do 88 | pct 0 ? width := pct 75 89 | pct 10 @+@ pct 5 @* 2 ? width := pct 80 90 | pct 100 @/ 2 ? width := pct 100 91 | 92 | describe "animation-name property" do 93 | 94 | let isRenderedFrom = isRenderedFromInline 95 | 96 | "animation-name:inherit" `isRenderedFrom` (animationName := inherit) 97 | 98 | "animation-name:initial" `isRenderedFrom` (animationName := initial) 99 | 100 | "animation-name:unset" `isRenderedFrom` (animationName := unset) 101 | 102 | "animation-name:none" `isRenderedFrom` (animationName := none) 103 | 104 | "animation-name:xx" `isRenderedFrom` (animationName := KeyframesName "xx") 105 | 106 | "animation-name:foo,none,bar" 107 | `isRenderedFrom` 108 | (animationName := KeyframesName "foo" /\ none /\ KeyframesName "bar") 109 | 110 | describe "animation-duration property" do 111 | 112 | let isRenderedFrom = isRenderedFromInline 113 | 114 | "animation-duration:inherit" 115 | `isRenderedFrom` 116 | (animationDuration := inherit) 117 | 118 | "animation-duration:initial" 119 | `isRenderedFrom` 120 | (animationDuration := initial) 121 | 122 | "animation-duration:unset" `isRenderedFrom` (animationDuration := unset) 123 | 124 | "animation-duration:150ms" `isRenderedFrom` (animationDuration := ms 150) 125 | 126 | "animation-duration:150ms,0,2s" 127 | `isRenderedFrom` 128 | (animationDuration := ms 150 /\ nil /\ sec 2) 129 | 130 | describe "animation-timing-function property" do 131 | 132 | let isRenderedFrom = isRenderedFromInline 133 | 134 | "animation-timing-function:inherit" 135 | `isRenderedFrom` 136 | (animationTimingFunction := inherit) 137 | 138 | "animation-timing-function:initial" 139 | `isRenderedFrom` 140 | (animationTimingFunction := initial) 141 | 142 | "animation-timing-function:unset" 143 | `isRenderedFrom` 144 | (animationTimingFunction := unset) 145 | 146 | "animation-timing-function:linear" 147 | `isRenderedFrom` 148 | (animationTimingFunction := linear) 149 | 150 | "animation-timing-function:ease" 151 | `isRenderedFrom` 152 | (animationTimingFunction := ease) 153 | 154 | "animation-timing-function:ease-in" 155 | `isRenderedFrom` 156 | (animationTimingFunction := easeIn) 157 | 158 | "animation-timing-function:ease-out" 159 | `isRenderedFrom` 160 | (animationTimingFunction := easeOut) 161 | 162 | "animation-timing-function:ease-in-out" 163 | `isRenderedFrom` 164 | (animationTimingFunction := easeInOut) 165 | 166 | "animation-timing-function:cubic-bezier(0.25,50,0.5,100)" 167 | `isRenderedFrom` 168 | (animationTimingFunction := cubicBezier 0.25 50 0.5 100) 169 | 170 | "animation-timing-function:steps(0,jump-start)" 171 | `isRenderedFrom` 172 | (animationTimingFunction := steps 0 jumpStart) 173 | 174 | "animation-timing-function:steps(1,jump-end)" 175 | `isRenderedFrom` 176 | (animationTimingFunction := steps 1 jumpEnd) 177 | 178 | "animation-timing-function:steps(2,jump-none)" 179 | `isRenderedFrom` 180 | (animationTimingFunction := steps 2 jumpNone) 181 | 182 | "animation-timing-function:steps(3,jump-both)" 183 | `isRenderedFrom` 184 | (animationTimingFunction := steps 3 jumpBoth) 185 | 186 | "animation-timing-function:steps(4,start)" 187 | `isRenderedFrom` 188 | (animationTimingFunction := steps 4 start) 189 | 190 | "animation-timing-function:steps(4,end)" 191 | `isRenderedFrom` 192 | (animationTimingFunction := steps 4 end) 193 | 194 | "animation-timing-function:step-start" 195 | `isRenderedFrom` 196 | (animationTimingFunction := stepStart) 197 | 198 | "animation-timing-function:step-end" 199 | `isRenderedFrom` 200 | (animationTimingFunction := stepEnd) 201 | 202 | "animation-timing-function:ease,step-start,cubic-bezier(0.1,0.7,1,0.1)" 203 | `isRenderedFrom` 204 | ( animationTimingFunction := 205 | ease /\ stepStart /\ cubicBezier 0.1 0.7 1.0 0.1 206 | ) 207 | 208 | describe "animation-iteration-count property" do 209 | 210 | let isRenderedFrom = isRenderedFromInline 211 | 212 | "animation-iteration-count:infinite" 213 | `isRenderedFrom` 214 | (animationIterationCount := infinite) 215 | 216 | "animation-iteration-count:3" 217 | `isRenderedFrom` 218 | (animationIterationCount := 3) 219 | 220 | "animation-iteration-count:3,infinite,2" 221 | `isRenderedFrom` 222 | (animationIterationCount := 3 /\ infinite /\ 2) 223 | 224 | describe "animation-direction property" do 225 | 226 | let isRenderedFrom = isRenderedFromInline 227 | 228 | "animation-direction:inherit" 229 | `isRenderedFrom` 230 | (animationDirection := inherit) 231 | 232 | "animation-direction:initial" 233 | `isRenderedFrom` 234 | (animationDirection := initial) 235 | 236 | "animation-direction:unset" `isRenderedFrom` (animationDirection := unset) 237 | 238 | "animation-direction:normal" 239 | `isRenderedFrom` 240 | (animationDirection := normal) 241 | 242 | "animation-direction:reverse" 243 | `isRenderedFrom` 244 | (animationDirection := reverse) 245 | 246 | "animation-direction:alternate" 247 | `isRenderedFrom` 248 | (animationDirection := alternate) 249 | 250 | "animation-direction:alternate-reverse" 251 | `isRenderedFrom` 252 | (animationDirection := alternateReverse) 253 | 254 | "animation-direction:normal,alternate-reverse,alternate" 255 | `isRenderedFrom` 256 | (animationDirection := normal /\ alternateReverse /\ alternate) 257 | 258 | describe "animation-play-state property" do 259 | 260 | let isRenderedFrom = isRenderedFromInline 261 | 262 | "animation-play-state:inherit" 263 | `isRenderedFrom` 264 | (animationPlayState := inherit) 265 | 266 | "animation-play-state:initial" 267 | `isRenderedFrom` 268 | (animationPlayState := initial) 269 | 270 | "animation-play-state:unset" 271 | `isRenderedFrom` 272 | (animationPlayState := unset) 273 | 274 | "animation-play-state:running" 275 | `isRenderedFrom` 276 | (animationPlayState := running) 277 | 278 | "animation-play-state:paused" 279 | `isRenderedFrom` 280 | (animationPlayState := paused) 281 | 282 | "animation-play-state:paused,running,running" 283 | `isRenderedFrom` 284 | (animationPlayState := paused /\ running /\ running) 285 | 286 | describe "animation-delay property" do 287 | 288 | let isRenderedFrom = isRenderedFromInline 289 | 290 | "animation-delay:inherit" `isRenderedFrom` (animationDelay := inherit) 291 | 292 | "animation-delay:initial" `isRenderedFrom` (animationDelay := initial) 293 | 294 | "animation-delay:unset" `isRenderedFrom` (animationDelay := unset) 295 | 296 | "animation-delay:150ms" `isRenderedFrom` (animationDelay := ms 150) 297 | 298 | "animation-delay:150ms,0,2s" 299 | `isRenderedFrom` 300 | (animationDelay := ms 150 /\ nil /\ sec 2) 301 | 302 | describe "animation-fill-mode property" do 303 | 304 | let isRenderedFrom = isRenderedFromInline 305 | 306 | "animation-fill-mode:inherit" 307 | `isRenderedFrom` 308 | (animationFillMode := inherit) 309 | 310 | "animation-fill-mode:initial" 311 | `isRenderedFrom` 312 | (animationFillMode := initial) 313 | 314 | "animation-fill-mode:unset" `isRenderedFrom` (animationFillMode := unset) 315 | 316 | "animation-fill-mode:none" `isRenderedFrom` (animationFillMode := none) 317 | 318 | "animation-fill-mode:forwards" 319 | `isRenderedFrom` 320 | (animationFillMode := forwards) 321 | 322 | "animation-fill-mode:backwards" 323 | `isRenderedFrom` 324 | (animationFillMode := backwards) 325 | 326 | "animation-fill-mode:both" `isRenderedFrom` (animationFillMode := both) 327 | 328 | "animation-fill-mode:none,backwards,both,forwards" 329 | `isRenderedFrom` 330 | (animationFillMode := none /\ backwards /\ both /\ forwards) 331 | -------------------------------------------------------------------------------- /test/BoxSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-box-3/ 2 | 3 | module Test.BoxSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( auto 9 | , em 10 | , inherit 11 | , initial 12 | , margin 13 | , marginBottom 14 | , marginLeft 15 | , marginRight 16 | , marginTop 17 | , padding 18 | , paddingBottom 19 | , paddingLeft 20 | , paddingRight 21 | , paddingTop 22 | , pct 23 | , px 24 | , unset 25 | , (:=) 26 | , (~) 27 | ) 28 | import Test.Spec (Spec, describe) 29 | import Test.Util (isRenderedFromInline) 30 | 31 | spec :: Spec Unit 32 | spec = do 33 | 34 | let isRenderedFrom = isRenderedFromInline 35 | 36 | describe "Box Module" do 37 | 38 | describe "margin-top property" do 39 | 40 | "margin-top:inherit" `isRenderedFrom` (marginTop := inherit) 41 | 42 | "margin-top:initial" `isRenderedFrom` (marginTop := initial) 43 | 44 | "margin-top:unset" `isRenderedFrom` (marginTop := unset) 45 | 46 | "margin-top:1px" `isRenderedFrom` (marginTop := px 1) 47 | 48 | "margin-top:10%" `isRenderedFrom` (marginTop := pct 10) 49 | 50 | "margin-top:auto" `isRenderedFrom` (marginTop := auto) 51 | 52 | describe "margin-right property" do 53 | 54 | "margin-right:inherit" `isRenderedFrom` (marginRight := inherit) 55 | 56 | "margin-right:initial" `isRenderedFrom` (marginRight := initial) 57 | 58 | "margin-right:unset" `isRenderedFrom` (marginRight := unset) 59 | 60 | "margin-right:1px" `isRenderedFrom` (marginRight := px 1) 61 | 62 | "margin-right:10%" `isRenderedFrom` (marginRight := pct 10) 63 | 64 | "margin-right:auto" `isRenderedFrom` (marginRight := auto) 65 | 66 | describe "margin-bottom property" do 67 | 68 | "margin-bottom:inherit" `isRenderedFrom` (marginBottom := inherit) 69 | 70 | "margin-bottom:initial" `isRenderedFrom` (marginBottom := initial) 71 | 72 | "margin-bottom:unset" `isRenderedFrom` (marginBottom := unset) 73 | 74 | "margin-bottom:1px" `isRenderedFrom` (marginBottom := px 1) 75 | 76 | "margin-bottom:10%" `isRenderedFrom` (marginBottom := pct 10) 77 | 78 | "margin-bottom:auto" `isRenderedFrom` (marginBottom := auto) 79 | 80 | describe "margin-left property" do 81 | 82 | "margin-left:inherit" `isRenderedFrom` (marginLeft := inherit) 83 | 84 | "margin-left:initial" `isRenderedFrom` (marginLeft := initial) 85 | 86 | "margin-left:unset" `isRenderedFrom` (marginLeft := unset) 87 | 88 | "margin-left:1px" `isRenderedFrom` (marginLeft := px 1) 89 | 90 | "margin-left:10%" `isRenderedFrom` (marginLeft := pct 10) 91 | 92 | "margin-left:auto" `isRenderedFrom` (marginLeft := auto) 93 | 94 | describe "margin property" do 95 | 96 | "margin:inherit" `isRenderedFrom` (margin := inherit) 97 | 98 | "margin:initial" `isRenderedFrom` (margin := initial) 99 | 100 | "margin:unset" `isRenderedFrom` (margin := unset) 101 | 102 | "margin:1px" `isRenderedFrom` (margin := px 1) 103 | 104 | "margin:10%" `isRenderedFrom` (margin := pct 10) 105 | 106 | "margin:1px 10%" `isRenderedFrom` (margin := px 1 ~ pct 10) 107 | 108 | "margin:10% 1px 25%" `isRenderedFrom` (margin := pct 10 ~ px 1 ~ pct 25) 109 | 110 | "margin:1px 1em 25% auto" 111 | `isRenderedFrom` 112 | (margin := px 1 ~ em 1 ~ pct 25 ~ auto) 113 | 114 | describe "padding-top property" do 115 | 116 | "padding-top:inherit" `isRenderedFrom` (paddingTop := inherit) 117 | 118 | "padding-top:initial" `isRenderedFrom` (paddingTop := initial) 119 | 120 | "padding-top:unset" `isRenderedFrom` (paddingTop := unset) 121 | 122 | "padding-top:1px" `isRenderedFrom` (paddingTop := px 1) 123 | 124 | "padding-top:10%" `isRenderedFrom` (paddingTop := pct 10) 125 | 126 | describe "padding-right property" do 127 | 128 | "padding-right:inherit" `isRenderedFrom` (paddingRight := inherit) 129 | 130 | "padding-right:initial" `isRenderedFrom` (paddingRight := initial) 131 | 132 | "padding-right:unset" `isRenderedFrom` (paddingRight := unset) 133 | 134 | "padding-right:1px" `isRenderedFrom` (paddingRight := px 1) 135 | 136 | "padding-right:10%" `isRenderedFrom` (paddingRight := pct 10) 137 | 138 | describe "padding-bottom property" do 139 | 140 | "padding-bottom:inherit" `isRenderedFrom` (paddingBottom := inherit) 141 | 142 | "padding-bottom:initial" `isRenderedFrom` (paddingBottom := initial) 143 | 144 | "padding-bottom:unset" `isRenderedFrom` (paddingBottom := unset) 145 | 146 | "padding-bottom:1px" `isRenderedFrom` (paddingBottom := px 1) 147 | 148 | "padding-bottom:10%" `isRenderedFrom` (paddingBottom := pct 10) 149 | 150 | describe "padding-left property" do 151 | 152 | "padding-left:inherit" `isRenderedFrom` (paddingLeft := inherit) 153 | 154 | "padding-left:initial" `isRenderedFrom` (paddingLeft := initial) 155 | 156 | "padding-left:unset" `isRenderedFrom` (paddingLeft := unset) 157 | 158 | "padding-left:1px" `isRenderedFrom` (paddingLeft := px 1) 159 | 160 | "padding-left:10%" `isRenderedFrom` (paddingLeft := pct 10) 161 | 162 | describe "padding property" do 163 | 164 | "padding:inherit" `isRenderedFrom` (padding := inherit) 165 | 166 | "padding:initial" `isRenderedFrom` (padding := initial) 167 | 168 | "padding:unset" `isRenderedFrom` (padding := unset) 169 | 170 | "padding:1px" `isRenderedFrom` (padding := px 1) 171 | 172 | "padding:10%" `isRenderedFrom` (padding := pct 10) 173 | 174 | "padding:10% 20%" `isRenderedFrom` (padding := pct 10 ~ pct 20) 175 | 176 | "padding:1px 10% 2em" `isRenderedFrom` (padding := px 1 ~ pct 10 ~ em 2) 177 | 178 | "padding:2em 1% 1px 10%" 179 | `isRenderedFrom` 180 | (padding := em 2 ~ pct 1 ~ px 1 ~ pct 10) 181 | -------------------------------------------------------------------------------- /test/ColorSpec.purs: -------------------------------------------------------------------------------- 1 | module Test.ColorSpec where 2 | 3 | import Prelude 4 | 5 | import Color (hsl) 6 | import Tecton 7 | ( color 8 | , currentColor 9 | , inherit 10 | , initial 11 | , opacity 12 | , transparent 13 | , unset 14 | , (:=) 15 | ) 16 | import Test.Spec (Spec, describe) 17 | import Test.Util (isRenderedFromInline) 18 | 19 | spec :: Spec Unit 20 | spec = do 21 | 22 | let isRenderedFrom = isRenderedFromInline 23 | 24 | describe "Color Module" do 25 | 26 | describe "color property" do 27 | 28 | "color:inherit" `isRenderedFrom` (color := inherit) 29 | 30 | "color:initial" `isRenderedFrom` (color := initial) 31 | 32 | "color:unset" `isRenderedFrom` (color := unset) 33 | 34 | "color:transparent" `isRenderedFrom` (color := transparent) 35 | 36 | "color:currentColor" `isRenderedFrom` (color := currentColor) 37 | 38 | "color:#0000ff" `isRenderedFrom` (color := hsl 240.0 1.0 0.5) 39 | 40 | describe "opacity property" do 41 | 42 | "opacity:inherit" `isRenderedFrom` (opacity := inherit) 43 | 44 | "opacity:initial" `isRenderedFrom` (opacity := initial) 45 | 46 | "opacity:unset" `isRenderedFrom` (opacity := unset) 47 | 48 | "opacity:0" `isRenderedFrom` (opacity := 0) 49 | 50 | "opacity:0.5" `isRenderedFrom` (opacity := 0.5) 51 | -------------------------------------------------------------------------------- /test/ContentSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-content-3/ 2 | 3 | module Test.ContentSpec where 4 | 5 | import Prelude 6 | 7 | import Color (rgb) 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( content 11 | , contents 12 | , inherit 13 | , initial 14 | , linearGradient 15 | , nil 16 | , none 17 | , normal 18 | , unset 19 | , url 20 | , (:=) 21 | ) 22 | import Test.Spec (Spec, describe) 23 | import Test.Util (isRenderedFromInline) 24 | 25 | spec :: Spec Unit 26 | spec = do 27 | 28 | let isRenderedFrom = isRenderedFromInline 29 | 30 | describe "Generated Content Module" do 31 | 32 | describe "content property" do 33 | 34 | "content:inherit" `isRenderedFrom` (content := inherit) 35 | 36 | "content:initial" `isRenderedFrom` (content := initial) 37 | 38 | "content:unset" `isRenderedFrom` (content := unset) 39 | 40 | "content:normal" `isRenderedFrom` (content := normal) 41 | 42 | "content:none" `isRenderedFrom` (content := none) 43 | 44 | "content:contents" `isRenderedFrom` (content := contents) 45 | 46 | "content:url(\"http://www.example.com/test.png\")" 47 | `isRenderedFrom` 48 | (content := url "http://www.example.com/test.png") 49 | 50 | "content:linear-gradient(0,#e66465,#9198e5)" 51 | `isRenderedFrom` 52 | (content := linearGradient nil $ rgb 230 100 101 /\ rgb 145 152 229) 53 | 54 | "content:url(\"http://www.example.com/test.png\")/\"This is the alt text\"" 55 | `isRenderedFrom` 56 | ( content := 57 | url "http://www.example.com/test.png" /\ "This is the alt text" 58 | ) 59 | 60 | "content:\"prefix\"" `isRenderedFrom` (content := "prefix") 61 | -------------------------------------------------------------------------------- /test/DisplaySpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-display-3/ 2 | 3 | module Test.DisplaySpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( block 9 | , contents 10 | , display 11 | , flex 12 | , flowRoot 13 | , grid 14 | , inherit 15 | , initial 16 | , inline 17 | , inlineBlock 18 | , inlineFlex 19 | , inlineGrid 20 | , inlineTable 21 | , listItem 22 | , none 23 | , table 24 | , tableCaption 25 | , tableCell 26 | , tableColumn 27 | , tableColumnGroup 28 | , tableFooterGroup 29 | , tableHeaderGroup 30 | , tableRow 31 | , tableRowGroup 32 | , unset 33 | , (:=) 34 | ) 35 | import Test.Spec (Spec, describe) 36 | import Test.Util (isRenderedFromInline) 37 | 38 | spec :: Spec Unit 39 | spec = do 40 | 41 | let isRenderedFrom = isRenderedFromInline 42 | 43 | describe "Display Module" do 44 | 45 | describe "display property" do 46 | 47 | "display:inherit" `isRenderedFrom` (display := inherit) 48 | 49 | "display:initial" `isRenderedFrom` (display := initial) 50 | 51 | "display:unset" `isRenderedFrom` (display := unset) 52 | 53 | "display:block" `isRenderedFrom` (display := block) 54 | 55 | "display:inline" `isRenderedFrom` (display := inline) 56 | 57 | "display:flow-root" `isRenderedFrom` (display := flowRoot) 58 | 59 | "display:table" `isRenderedFrom` (display := table) 60 | 61 | "display:flex" `isRenderedFrom` (display := flex) 62 | 63 | "display:grid" `isRenderedFrom` (display := grid) 64 | 65 | "display:list-item" `isRenderedFrom` (display := listItem) 66 | 67 | "display:table-row-group" `isRenderedFrom` (display := tableRowGroup) 68 | 69 | "display:table-header-group" 70 | `isRenderedFrom` 71 | (display := tableHeaderGroup) 72 | 73 | "display:table-footer-group" 74 | `isRenderedFrom` 75 | (display := tableFooterGroup) 76 | 77 | "display:table-row" `isRenderedFrom` (display := tableRow) 78 | 79 | "display:table-cell" `isRenderedFrom` (display := tableCell) 80 | 81 | "display:table-column-group" 82 | `isRenderedFrom` 83 | (display := tableColumnGroup) 84 | 85 | "display:table-column" `isRenderedFrom` (display := tableColumn) 86 | 87 | "display:table-caption" `isRenderedFrom` (display := tableCaption) 88 | 89 | "display:contents" `isRenderedFrom` (display := contents) 90 | 91 | "display:none" `isRenderedFrom` (display := none) 92 | 93 | "display:inline-block" `isRenderedFrom` (display := inlineBlock) 94 | 95 | "display:inline-table" `isRenderedFrom` (display := inlineTable) 96 | 97 | "display:inline-flex" `isRenderedFrom` (display := inlineFlex) 98 | 99 | "display:inline-grid" `isRenderedFrom` (display := inlineGrid) 100 | -------------------------------------------------------------------------------- /test/FlexboxSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-flexbox-1/ 2 | 3 | module Test.FlexboxSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( alignContent 9 | , alignItems 10 | , alignSelf 11 | , auto 12 | , baseline 13 | , center 14 | , column 15 | , columnReverse 16 | , content 17 | , em 18 | , fitContent 19 | , flex 20 | , flexBasis 21 | , flexDirection 22 | , flexEnd 23 | , flexGrow 24 | , flexShrink 25 | , flexStart 26 | , flexWrap 27 | , inherit 28 | , initial 29 | , justifyContent 30 | , maxContent 31 | , minContent 32 | , none 33 | , nowrap 34 | , order 35 | , pct 36 | , px 37 | , row 38 | , rowReverse 39 | , spaceAround 40 | , spaceBetween 41 | , stretch 42 | , unset 43 | , wrap 44 | , wrapReverse 45 | , (:=) 46 | , (~) 47 | ) 48 | import Test.Spec (Spec, describe) 49 | import Test.Util (isRenderedFromInline) 50 | 51 | spec :: Spec Unit 52 | spec = do 53 | 54 | let isRenderedFrom = isRenderedFromInline 55 | 56 | describe "Flexible Box Layout Module" do 57 | 58 | describe "flex-direction property" do 59 | 60 | "flex-direction:inherit" `isRenderedFrom` (flexDirection := inherit) 61 | 62 | "flex-direction:initial" `isRenderedFrom` (flexDirection := initial) 63 | 64 | "flex-direction:unset" `isRenderedFrom` (flexDirection := unset) 65 | 66 | "flex-direction:row" `isRenderedFrom` (flexDirection := row) 67 | 68 | "flex-direction:row-reverse" 69 | `isRenderedFrom` 70 | (flexDirection := rowReverse) 71 | 72 | "flex-direction:column" `isRenderedFrom` (flexDirection := column) 73 | 74 | "flex-direction:column-reverse" 75 | `isRenderedFrom` 76 | (flexDirection := columnReverse) 77 | 78 | "flex-direction:column-reverse" 79 | `isRenderedFrom` 80 | (flexDirection := columnReverse) 81 | 82 | describe "flex-wrap property" do 83 | 84 | "flex-wrap:inherit" `isRenderedFrom` (flexWrap := inherit) 85 | 86 | "flex-wrap:initial" `isRenderedFrom` (flexWrap := initial) 87 | 88 | "flex-wrap:unset" `isRenderedFrom` (flexWrap := unset) 89 | 90 | "flex-wrap:nowrap" `isRenderedFrom` (flexWrap := nowrap) 91 | 92 | "flex-wrap:wrap" `isRenderedFrom` (flexWrap := wrap) 93 | 94 | "flex-wrap:wrap-reverse" `isRenderedFrom` (flexWrap := wrapReverse) 95 | 96 | describe "order property" do 97 | 98 | "order:inherit" `isRenderedFrom` (order := inherit) 99 | 100 | "order:initial" `isRenderedFrom` (order := initial) 101 | 102 | "order:unset" `isRenderedFrom` (order := unset) 103 | 104 | "order:12" `isRenderedFrom` (order := 12) 105 | 106 | describe "flex property" do 107 | 108 | "flex:inherit" `isRenderedFrom` (flex := inherit) 109 | 110 | "flex:initial" `isRenderedFrom` (flex := initial) 111 | 112 | "flex:unset" `isRenderedFrom` (flex := unset) 113 | 114 | "flex:none" `isRenderedFrom` (flex := none) 115 | 116 | "flex:10em" `isRenderedFrom` (flex := em 10) 117 | 118 | "flex:30%" `isRenderedFrom` (flex := pct 30) 119 | 120 | "flex:fit-content(25%)" `isRenderedFrom` (flex := fitContent $ pct 25) 121 | 122 | "flex:1.5 max-content" `isRenderedFrom` (flex := 1.5 ~ maxContent) 123 | 124 | "flex:2 100px" `isRenderedFrom` (flex := 2 ~ px 100) 125 | 126 | "flex:1 10%" `isRenderedFrom` (flex := 1 ~ pct 10) 127 | 128 | "flex:3.5 2.5" `isRenderedFrom` (flex := 3.5 ~ 2.5) 129 | 130 | "flex:0 0 auto" `isRenderedFrom` (flex := 0 ~ 0 ~ auto) 131 | 132 | "flex:1 1 100px" `isRenderedFrom` (flex := 1.0 ~ 1.0 ~ px 100) 133 | 134 | "flex:3 2 10%" `isRenderedFrom` (flex := 3 ~ 2 ~ pct 10) 135 | 136 | describe "flex-grow property" do 137 | 138 | "flex-grow:inherit" `isRenderedFrom` (flexGrow := inherit) 139 | 140 | "flex-grow:initial" `isRenderedFrom` (flexGrow := initial) 141 | 142 | "flex-grow:unset" `isRenderedFrom` (flexGrow := unset) 143 | 144 | "flex-grow:1" `isRenderedFrom` (flexGrow := 1) 145 | 146 | "flex-grow:1.5" `isRenderedFrom` (flexGrow := 1.5) 147 | 148 | describe "flex-shrink property" do 149 | 150 | "flex-shrink:inherit" `isRenderedFrom` (flexShrink := inherit) 151 | 152 | "flex-shrink:initial" `isRenderedFrom` (flexShrink := initial) 153 | 154 | "flex-shrink:unset" `isRenderedFrom` (flexShrink := unset) 155 | 156 | "flex-shrink:1" `isRenderedFrom` (flexShrink := 1) 157 | 158 | "flex-shrink:1.5" `isRenderedFrom` (flexShrink := 1.5) 159 | 160 | describe "flex-basis property" do 161 | 162 | "flex-basis:inherit" `isRenderedFrom` (flexBasis := inherit) 163 | 164 | "flex-basis:initial" `isRenderedFrom` (flexBasis := initial) 165 | 166 | "flex-basis:unset" `isRenderedFrom` (flexBasis := unset) 167 | 168 | "flex-basis:10em" `isRenderedFrom` (flexBasis := em 10) 169 | 170 | "flex-basis:3px" `isRenderedFrom` (flexBasis := px 3) 171 | 172 | "flex-basis:50%" `isRenderedFrom` (flexBasis := pct 50) 173 | 174 | "flex-basis:auto" `isRenderedFrom` (flexBasis := auto) 175 | 176 | "flex-basis:max-content" `isRenderedFrom` (flexBasis := maxContent) 177 | 178 | "flex-basis:min-content" `isRenderedFrom` (flexBasis := minContent) 179 | 180 | "flex-basis:content" `isRenderedFrom` (flexBasis := content) 181 | 182 | describe "justify-content property" do 183 | 184 | "justify-content:inherit" `isRenderedFrom` (justifyContent := inherit) 185 | 186 | "justify-content:initial" `isRenderedFrom` (justifyContent := initial) 187 | 188 | "justify-content:unset" `isRenderedFrom` (justifyContent := unset) 189 | 190 | "justify-content:flex-start" 191 | `isRenderedFrom` 192 | (justifyContent := flexStart) 193 | 194 | "justify-content:flex-end" `isRenderedFrom` (justifyContent := flexEnd) 195 | 196 | "justify-content:center" `isRenderedFrom` (justifyContent := center) 197 | 198 | "justify-content:space-between" 199 | `isRenderedFrom` 200 | (justifyContent := spaceBetween) 201 | 202 | "justify-content:space-around" 203 | `isRenderedFrom` 204 | (justifyContent := spaceAround) 205 | 206 | describe "align-items property" do 207 | 208 | "align-items:inherit" `isRenderedFrom` (alignItems := inherit) 209 | 210 | "align-items:initial" `isRenderedFrom` (alignItems := initial) 211 | 212 | "align-items:unset" `isRenderedFrom` (alignItems := unset) 213 | 214 | "align-items:flex-start" `isRenderedFrom` (alignItems := flexStart) 215 | 216 | "align-items:flex-end" `isRenderedFrom` (alignItems := flexEnd) 217 | 218 | "align-items:center" `isRenderedFrom` (alignItems := center) 219 | 220 | "align-items:baseline" `isRenderedFrom` (alignItems := baseline) 221 | 222 | "align-items:stretch" `isRenderedFrom` (alignItems := stretch) 223 | 224 | describe "align-self property" do 225 | 226 | "align-self:inherit" `isRenderedFrom` (alignSelf := inherit) 227 | 228 | "align-self:initial" `isRenderedFrom` (alignSelf := initial) 229 | 230 | "align-self:unset" `isRenderedFrom` (alignSelf := unset) 231 | 232 | "align-self:auto" `isRenderedFrom` (alignSelf := auto) 233 | 234 | "align-self:flex-start" `isRenderedFrom` (alignSelf := flexStart) 235 | 236 | "align-self:flex-end" `isRenderedFrom` (alignSelf := flexEnd) 237 | 238 | "align-self:center" `isRenderedFrom` (alignSelf := center) 239 | 240 | "align-self:baseline" `isRenderedFrom` (alignSelf := baseline) 241 | 242 | "align-self:stretch" `isRenderedFrom` (alignSelf := stretch) 243 | 244 | describe "align-content property" do 245 | 246 | "align-content:inherit" `isRenderedFrom` (alignContent := inherit) 247 | 248 | "align-content:initial" `isRenderedFrom` (alignContent := initial) 249 | 250 | "align-content:unset" `isRenderedFrom` (alignContent := unset) 251 | 252 | "align-content:flex-start" `isRenderedFrom` (alignContent := flexStart) 253 | 254 | "align-content:flex-end" `isRenderedFrom` (alignContent := flexEnd) 255 | 256 | "align-content:center" `isRenderedFrom` (alignContent := center) 257 | 258 | "align-content:space-between" 259 | `isRenderedFrom` 260 | (alignContent := spaceBetween) 261 | 262 | "align-content:space-around" 263 | `isRenderedFrom` 264 | (alignContent := spaceAround) 265 | 266 | "align-content:stretch" `isRenderedFrom` (alignContent := stretch) 267 | -------------------------------------------------------------------------------- /test/ImagesSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-images-3/ 2 | 3 | module Test.ImagesSpec where 4 | 5 | import Prelude hiding (bottom, top) 6 | 7 | import Color (black, rgb) 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( bottom 11 | , center 12 | , circle 13 | , closestSide 14 | , currentColor 15 | , deg 16 | , ellipse 17 | , farthestCorner 18 | , left 19 | , linearGradient 20 | , nil 21 | , pct 22 | , px 23 | , radialGradient 24 | , repeating 25 | , right 26 | , top 27 | , transparent 28 | , (~) 29 | ) 30 | import Test.Spec (Spec, describe) 31 | import Test.Util (isRenderedFromVal) 32 | 33 | spec :: Spec Unit 34 | spec = do 35 | 36 | let isRenderedFrom = isRenderedFromVal 37 | 38 | describe "Images Module" do 39 | 40 | describe "linear-gradient()" do 41 | 42 | "linear-gradient(0,#0000ff,#008000)" 43 | `isRenderedFrom` do 44 | linearGradient nil $ rgb 0 0 255 /\ rgb 0 128 0 45 | 46 | "linear-gradient(45deg,#0000ff,#008000)" 47 | `isRenderedFrom` do 48 | linearGradient (deg 45) $ rgb 0 0 255 /\ rgb 0 128 0 49 | 50 | "linear-gradient(90deg,#0000ff,#008000 40%,#ff0000)" 51 | `isRenderedFrom` do 52 | linearGradient (deg 90) 53 | $ rgb 0 0 255 /\ rgb 0 128 0 ~ pct 40 /\ rgb 255 0 0 54 | 55 | "linear-gradient(45deg,currentColor,10%,transparent)" 56 | `isRenderedFrom` do 57 | linearGradient (deg 45) $ currentColor /\ pct 10 /\ transparent 58 | 59 | describe "radial-gradient()" do 60 | 61 | "radial-gradient(circle at center,#000080,30px,#800000)" 62 | `isRenderedFrom` do 63 | radialGradient circle center $ rgb 0 0 128 /\ px 30 /\ rgb 128 0 0 64 | 65 | "radial-gradient(ellipse at center,#ff9900,#0099ff)" 66 | `isRenderedFrom` do 67 | radialGradient ellipse center $ rgb 255 153 0 /\ rgb 0 153 255 68 | 69 | "radial-gradient(circle farthest-corner at center,#ffff00,#000000)" 70 | `isRenderedFrom` do 71 | radialGradient (circle ~ farthestCorner) center 72 | $ rgb 255 255 0 /\ black 73 | 74 | "radial-gradient(ellipse farthest-corner at left top,#808000,#800080)" 75 | `isRenderedFrom` do 76 | radialGradient (ellipse ~ farthestCorner) (left ~ top) 77 | $ rgb 128 128 0 /\ rgb 128 0 128 78 | 79 | "radial-gradient(10px at left,#009900,#009999)" 80 | `isRenderedFrom` do 81 | radialGradient (px 10) left $ rgb 0 153 0 /\ rgb 0 153 153 82 | 83 | "radial-gradient(10px 20px at right bottom,#000000,transparent)" 84 | `isRenderedFrom` do 85 | radialGradient (px 10 ~ px 20) (right ~ bottom) $ black /\ transparent 86 | 87 | "radial-gradient(10% 20% at top,#ff0000,#0000ff)" 88 | `isRenderedFrom` do 89 | radialGradient (pct 10 ~ pct 20) top $ rgb 255 0 0 /\ rgb 0 0 255 90 | 91 | describe "repeating-linear-gradient()" do 92 | 93 | "repeating-linear-gradient(0,#ff0000,#0000ff 20px,#ff0000 40px)" 94 | `isRenderedFrom` do 95 | repeating 96 | $ linearGradient nil 97 | $ rgb 255 0 0 /\ rgb 0 0 255 ~ px 20 /\ rgb 255 0 0 ~ px 40 98 | 99 | "repeating-linear-gradient(90deg,#ff0000,#0000ff 20px,#ff0000 40px)" 100 | `isRenderedFrom` do 101 | repeating $ linearGradient (deg 90) 102 | $ rgb 255 0 0 /\ rgb 0 0 255 ~ px 20 /\ rgb 255 0 0 ~ px 40 103 | 104 | describe "repeating-radial-gradient()" do 105 | 106 | "repeating-radial-gradient(circle closest-side at 20px 30px,#ff0000,#ffff00,#00ff00 100%,#ffff00 150%,#ff0000 200%)" 107 | `isRenderedFrom` do 108 | repeating $ radialGradient (circle ~ closestSide) (px 20 ~ px 30) $ 109 | rgb 255 0 0 110 | /\ rgb 255 255 0 111 | /\ rgb 0 255 0 ~ pct 100 112 | /\ rgb 255 255 0 ~ pct 150 113 | /\ rgb 255 0 0 ~ pct 200 114 | -------------------------------------------------------------------------------- /test/InlineSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-inline-3/ 2 | 3 | module Test.InlineSpec where 4 | 5 | import Prelude hiding (bottom, sub, top) 6 | 7 | import Tecton 8 | ( alignmentBaseline 9 | , alphabetic 10 | , auto 11 | , baseline 12 | , baselineShift 13 | , baselineSource 14 | , bottom 15 | , center 16 | , central 17 | , dominantBaseline 18 | , first 19 | , hanging 20 | , ideographic 21 | , inherit 22 | , initial 23 | , last 24 | , lineHeight 25 | , mathematical 26 | , middle 27 | , normal 28 | , pct 29 | , px 30 | , sub 31 | , super 32 | , textBottom 33 | , textTop 34 | , top 35 | , unset 36 | , verticalAlign 37 | , (:=) 38 | , (~) 39 | ) 40 | import Test.Spec (Spec, describe) 41 | import Test.Util (isRenderedFromInline) 42 | 43 | spec :: Spec Unit 44 | spec = do 45 | 46 | let isRenderedFrom = isRenderedFromInline 47 | 48 | describe "Inline Layout Module" do 49 | 50 | describe "dominant-baseline property" do 51 | 52 | "dominant-baseline:inherit" `isRenderedFrom` (dominantBaseline := inherit) 53 | 54 | "dominant-baseline:initial" `isRenderedFrom` (dominantBaseline := initial) 55 | 56 | "dominant-baseline:unset" `isRenderedFrom` (dominantBaseline := unset) 57 | 58 | "dominant-baseline:auto" `isRenderedFrom` (dominantBaseline := auto) 59 | 60 | "dominant-baseline:text-bottom" 61 | `isRenderedFrom` 62 | (dominantBaseline := textBottom) 63 | 64 | "dominant-baseline:alphabetic" 65 | `isRenderedFrom` 66 | (dominantBaseline := alphabetic) 67 | 68 | "dominant-baseline:ideographic" 69 | `isRenderedFrom` 70 | (dominantBaseline := ideographic) 71 | 72 | "dominant-baseline:middle" `isRenderedFrom` (dominantBaseline := middle) 73 | 74 | "dominant-baseline:central" `isRenderedFrom` (dominantBaseline := central) 75 | 76 | "dominant-baseline:mathematical" 77 | `isRenderedFrom` 78 | (dominantBaseline := mathematical) 79 | 80 | "dominant-baseline:hanging" `isRenderedFrom` (dominantBaseline := hanging) 81 | 82 | "dominant-baseline:text-top" 83 | `isRenderedFrom` 84 | (dominantBaseline := textTop) 85 | 86 | describe "vertical-align property" do 87 | 88 | "vertical-align:inherit" `isRenderedFrom` (verticalAlign := inherit) 89 | 90 | "vertical-align:initial" `isRenderedFrom` (verticalAlign := initial) 91 | 92 | "vertical-align:unset" `isRenderedFrom` (verticalAlign := unset) 93 | 94 | "vertical-align:first central sub" 95 | `isRenderedFrom` 96 | (verticalAlign := first ~ central ~ sub) 97 | 98 | "vertical-align:last middle super" 99 | `isRenderedFrom` 100 | (verticalAlign := last ~ middle ~ super) 101 | 102 | "vertical-align:first baseline bottom" 103 | `isRenderedFrom` 104 | (verticalAlign := first ~ baseline ~ bottom) 105 | 106 | "vertical-align:last text-bottom 10px" 107 | `isRenderedFrom` 108 | (verticalAlign := last ~ textBottom ~ px 10) 109 | 110 | "vertical-align:first central" 111 | `isRenderedFrom` 112 | (verticalAlign := first ~ central) 113 | 114 | "vertical-align:last text-top" 115 | `isRenderedFrom` 116 | (verticalAlign := last ~ textTop) 117 | 118 | "vertical-align:first middle" 119 | `isRenderedFrom` 120 | (verticalAlign := first ~ middle) 121 | 122 | "vertical-align:first baseline" 123 | `isRenderedFrom` 124 | (verticalAlign := first ~ baseline) 125 | 126 | "vertical-align:last baseline" 127 | `isRenderedFrom` 128 | (verticalAlign := last ~ baseline) 129 | 130 | "vertical-align:first sub" 131 | `isRenderedFrom` 132 | (verticalAlign := first ~ sub) 133 | 134 | "vertical-align:last 10%" 135 | `isRenderedFrom` 136 | (verticalAlign := last ~ pct 10) 137 | 138 | "vertical-align:ideographic top" 139 | `isRenderedFrom` 140 | (verticalAlign := ideographic ~ top) 141 | 142 | "vertical-align:mathematical super" 143 | `isRenderedFrom` 144 | (verticalAlign := mathematical ~ super) 145 | 146 | "vertical-align:middle center" 147 | `isRenderedFrom` 148 | (verticalAlign := middle ~ center) 149 | 150 | "vertical-align:baseline 1px" 151 | `isRenderedFrom` 152 | (verticalAlign := baseline ~ px 1) 153 | 154 | "vertical-align:first" `isRenderedFrom` (verticalAlign := first) 155 | 156 | "vertical-align:last" `isRenderedFrom` (verticalAlign := last) 157 | 158 | "vertical-align:baseline" `isRenderedFrom` (verticalAlign := baseline) 159 | 160 | "vertical-align:text-bottom" 161 | `isRenderedFrom` 162 | (verticalAlign := textBottom) 163 | 164 | "vertical-align:alphabetic" `isRenderedFrom` (verticalAlign := alphabetic) 165 | 166 | "vertical-align:ideographic" 167 | `isRenderedFrom` 168 | (verticalAlign := ideographic) 169 | 170 | "vertical-align:middle" `isRenderedFrom` (verticalAlign := middle) 171 | 172 | "vertical-align:central" `isRenderedFrom` (verticalAlign := central) 173 | 174 | "vertical-align:mathematical" 175 | `isRenderedFrom` 176 | (verticalAlign := mathematical) 177 | 178 | "vertical-align:text-top" `isRenderedFrom` (verticalAlign := textTop) 179 | 180 | "vertical-align:10px" `isRenderedFrom` (verticalAlign := px 10) 181 | 182 | "vertical-align:5%" `isRenderedFrom` (verticalAlign := pct 5) 183 | 184 | "vertical-align:sub" `isRenderedFrom` (verticalAlign := sub) 185 | 186 | "vertical-align:super" `isRenderedFrom` (verticalAlign := super) 187 | 188 | "vertical-align:top" `isRenderedFrom` (verticalAlign := top) 189 | 190 | "vertical-align:center" `isRenderedFrom` (verticalAlign := center) 191 | 192 | "vertical-align:bottom" `isRenderedFrom` (verticalAlign := bottom) 193 | 194 | describe "baseline-source property" do 195 | 196 | "baseline-source:inherit" `isRenderedFrom` (baselineSource := inherit) 197 | 198 | "baseline-source:initial" `isRenderedFrom` (baselineSource := initial) 199 | 200 | "baseline-source:unset" `isRenderedFrom` (baselineSource := unset) 201 | 202 | "baseline-source:auto" `isRenderedFrom` (baselineSource := auto) 203 | 204 | "baseline-source:first" `isRenderedFrom` (baselineSource := first) 205 | 206 | "baseline-source:last" `isRenderedFrom` (baselineSource := last) 207 | 208 | describe "alignment-baseline property" do 209 | 210 | "alignment-baseline:inherit" 211 | `isRenderedFrom` 212 | (alignmentBaseline := inherit) 213 | 214 | "alignment-baseline:initial" 215 | `isRenderedFrom` 216 | (alignmentBaseline := initial) 217 | 218 | "alignment-baseline:unset" 219 | `isRenderedFrom` 220 | (alignmentBaseline := unset) 221 | 222 | "alignment-baseline:baseline" 223 | `isRenderedFrom` 224 | (alignmentBaseline := baseline) 225 | 226 | "alignment-baseline:text-bottom" 227 | `isRenderedFrom` 228 | (alignmentBaseline := textBottom) 229 | 230 | "alignment-baseline:alphabetic" 231 | `isRenderedFrom` 232 | (alignmentBaseline := alphabetic) 233 | 234 | "alignment-baseline:ideographic" 235 | `isRenderedFrom` 236 | (alignmentBaseline := ideographic) 237 | 238 | "alignment-baseline:middle" 239 | `isRenderedFrom` 240 | (alignmentBaseline := middle) 241 | 242 | "alignment-baseline:central" 243 | `isRenderedFrom` 244 | (alignmentBaseline := central) 245 | 246 | "alignment-baseline:mathematical" 247 | `isRenderedFrom` 248 | (alignmentBaseline := mathematical) 249 | 250 | "alignment-baseline:text-top" 251 | `isRenderedFrom` 252 | (alignmentBaseline := textTop) 253 | 254 | describe "baseline-shift property" do 255 | 256 | "baseline-shift:inherit" `isRenderedFrom` (baselineShift := inherit) 257 | 258 | "baseline-shift:initial" `isRenderedFrom` (baselineShift := initial) 259 | 260 | "baseline-shift:unset" `isRenderedFrom` (baselineShift := unset) 261 | 262 | "baseline-shift:1px" `isRenderedFrom` (baselineShift := px 1) 263 | 264 | "baseline-shift:-5%" `isRenderedFrom` (baselineShift := pct (-5)) 265 | 266 | "baseline-shift:sub" `isRenderedFrom` (baselineShift := sub) 267 | 268 | "baseline-shift:super" `isRenderedFrom` (baselineShift := super) 269 | 270 | "baseline-shift:top" `isRenderedFrom` (baselineShift := top) 271 | 272 | "baseline-shift:center" `isRenderedFrom` (baselineShift := center) 273 | 274 | "baseline-shift:bottom" `isRenderedFrom` (baselineShift := bottom) 275 | 276 | describe "line-height property" do 277 | 278 | "line-height:inherit" `isRenderedFrom` (lineHeight := inherit) 279 | 280 | "line-height:initial" `isRenderedFrom` (lineHeight := initial) 281 | 282 | "line-height:unset" `isRenderedFrom` (lineHeight := unset) 283 | 284 | "line-height:normal" `isRenderedFrom` (lineHeight := normal) 285 | 286 | "line-height:1" `isRenderedFrom` (lineHeight := 1) 287 | 288 | "line-height:1.5" `isRenderedFrom` (lineHeight := 1.5) 289 | 290 | "line-height:24px" `isRenderedFrom` (lineHeight := px 24) 291 | 292 | "line-height:125%" `isRenderedFrom` (lineHeight := pct 125) 293 | -------------------------------------------------------------------------------- /test/ListsSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-lists-3/ 2 | 3 | module Test.ListsSpec where 4 | 5 | import Prelude 6 | 7 | import Color (rgb) 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( arabicIndic 11 | , armenian 12 | , bengali 13 | , cambodian 14 | , center 15 | , circle 16 | , cjkDecimal 17 | , cjkEarthlyBranch 18 | , cjkHeavenlyStem 19 | , decimal 20 | , decimalLeadingZero 21 | , devanagari 22 | , disc 23 | , disclosureClosed 24 | , disclosureOpen 25 | , georgian 26 | , gujarati 27 | , gurmukhi 28 | , hebrew 29 | , hiragana 30 | , hiraganaIroha 31 | , inherit 32 | , initial 33 | , inside 34 | , kannada 35 | , katakana 36 | , katakanaIroha 37 | , khmer 38 | , lao 39 | , li 40 | , listStyleImage 41 | , listStylePosition 42 | , listStyleType 43 | , lowerAlpha 44 | , lowerArmenian 45 | , lowerGreek 46 | , lowerLatin 47 | , lowerRoman 48 | , malayalam 49 | , marker 50 | , mongolian 51 | , myanmar 52 | , nil 53 | , none 54 | , oriya 55 | , outside 56 | , persian 57 | , radialGradient 58 | , square 59 | , tamil 60 | , telugu 61 | , thai 62 | , tibetan 63 | , unset 64 | , upperAlpha 65 | , upperArmenian 66 | , upperLatin 67 | , upperRoman 68 | , url 69 | , width 70 | , (&::) 71 | , (:=) 72 | , (?) 73 | ) 74 | import Test.Spec (Spec, describe) 75 | import Test.Util (isRenderedFromInline, isRenderedFromSheet) 76 | 77 | spec :: Spec Unit 78 | spec = 79 | describe "Lists and Counters Module" do 80 | 81 | describe "marker pseudo-element" do 82 | 83 | let isRenderedFrom = isRenderedFromSheet 84 | 85 | "li::marker{width:0}" 86 | `isRenderedFrom` do 87 | li &:: marker ? width := nil 88 | 89 | describe "list-style-image property" do 90 | 91 | let isRenderedFrom = isRenderedFromInline 92 | 93 | "list-style-image:inherit" `isRenderedFrom` (listStyleImage := inherit) 94 | 95 | "list-style-image:initial" `isRenderedFrom` (listStyleImage := initial) 96 | 97 | "list-style-image:unset" `isRenderedFrom` (listStyleImage := unset) 98 | 99 | "list-style-image:none" `isRenderedFrom` (listStyleImage := none) 100 | 101 | "list-style-image:url(\"http://example.com/ellipse.png\")" 102 | `isRenderedFrom` 103 | (listStyleImage := url "http://example.com/ellipse.png") 104 | 105 | "list-style-image:radial-gradient(circle at center,#0000ff,#ffff00)" 106 | `isRenderedFrom` 107 | ( listStyleImage := 108 | radialGradient circle center $ rgb 0 0 255 /\ rgb 255 255 0 109 | ) 110 | 111 | describe "list-style-type property" do 112 | 113 | let isRenderedFrom = isRenderedFromInline 114 | 115 | "list-style-type:inherit" `isRenderedFrom` (listStyleType := inherit) 116 | 117 | "list-style-type:initial" `isRenderedFrom` (listStyleType := initial) 118 | 119 | "list-style-type:unset" `isRenderedFrom` (listStyleType := unset) 120 | 121 | "list-style-type:decimal" `isRenderedFrom` (listStyleType := decimal) 122 | 123 | "list-style-type:decimal-leading-zero" 124 | `isRenderedFrom` 125 | (listStyleType := decimalLeadingZero) 126 | 127 | "list-style-type:arabic-indic" 128 | `isRenderedFrom` 129 | (listStyleType := arabicIndic) 130 | 131 | "list-style-type:armenian" 132 | `isRenderedFrom` 133 | (listStyleType := armenian) 134 | 135 | "list-style-type:upper-armenian" 136 | `isRenderedFrom` 137 | (listStyleType := upperArmenian) 138 | 139 | "list-style-type:lower-armenian" 140 | `isRenderedFrom` 141 | (listStyleType := lowerArmenian) 142 | 143 | "list-style-type:bengali" `isRenderedFrom` (listStyleType := bengali) 144 | 145 | "list-style-type:cambodian" `isRenderedFrom` (listStyleType := cambodian) 146 | 147 | "list-style-type:khmer" `isRenderedFrom` (listStyleType := khmer) 148 | 149 | "list-style-type:cjk-decimal" 150 | `isRenderedFrom` 151 | (listStyleType := cjkDecimal) 152 | 153 | "list-style-type:devanagari" 154 | `isRenderedFrom` 155 | (listStyleType := devanagari) 156 | 157 | "list-style-type:georgian" `isRenderedFrom` (listStyleType := georgian) 158 | 159 | "list-style-type:gujarati" `isRenderedFrom` (listStyleType := gujarati) 160 | 161 | "list-style-type:gurmukhi" `isRenderedFrom` (listStyleType := gurmukhi) 162 | 163 | "list-style-type:hebrew" `isRenderedFrom` (listStyleType := hebrew) 164 | 165 | "list-style-type:kannada" `isRenderedFrom` (listStyleType := kannada) 166 | 167 | "list-style-type:lao" `isRenderedFrom` (listStyleType := lao) 168 | 169 | "list-style-type:malayalam" `isRenderedFrom` (listStyleType := malayalam) 170 | 171 | "list-style-type:mongolian" `isRenderedFrom` (listStyleType := mongolian) 172 | 173 | "list-style-type:myanmar" `isRenderedFrom` (listStyleType := myanmar) 174 | 175 | "list-style-type:oriya" `isRenderedFrom` (listStyleType := oriya) 176 | 177 | "list-style-type:persian" `isRenderedFrom` (listStyleType := persian) 178 | 179 | "list-style-type:lower-roman" 180 | `isRenderedFrom` 181 | (listStyleType := lowerRoman) 182 | 183 | "list-style-type:upper-roman" 184 | `isRenderedFrom` 185 | (listStyleType := upperRoman) 186 | 187 | "list-style-type:tamil" `isRenderedFrom` (listStyleType := tamil) 188 | 189 | "list-style-type:telugu" `isRenderedFrom` (listStyleType := telugu) 190 | 191 | "list-style-type:thai" `isRenderedFrom` (listStyleType := thai) 192 | 193 | "list-style-type:tibetan" `isRenderedFrom` (listStyleType := tibetan) 194 | 195 | "list-style-type:lower-alpha" 196 | `isRenderedFrom` 197 | (listStyleType := lowerAlpha) 198 | 199 | "list-style-type:lower-latin" 200 | `isRenderedFrom` 201 | (listStyleType := lowerLatin) 202 | 203 | "list-style-type:upper-alpha" 204 | `isRenderedFrom` 205 | (listStyleType := upperAlpha) 206 | 207 | "list-style-type:upper-latin" 208 | `isRenderedFrom` 209 | (listStyleType := upperLatin) 210 | 211 | "list-style-type:lower-greek" 212 | `isRenderedFrom` 213 | (listStyleType := lowerGreek) 214 | 215 | "list-style-type:hiragana" 216 | `isRenderedFrom` 217 | (listStyleType := hiragana) 218 | 219 | "list-style-type:hiragana-iroha" 220 | `isRenderedFrom` 221 | (listStyleType := hiraganaIroha) 222 | 223 | "list-style-type:katakana" 224 | `isRenderedFrom` 225 | (listStyleType := katakana) 226 | 227 | "list-style-type:katakana-iroha" 228 | `isRenderedFrom` 229 | (listStyleType := katakanaIroha) 230 | 231 | "list-style-type:disc" `isRenderedFrom` (listStyleType := disc) 232 | 233 | "list-style-type:circle" `isRenderedFrom` (listStyleType := circle) 234 | 235 | "list-style-type:square" `isRenderedFrom` (listStyleType := square) 236 | 237 | "list-style-type:disclosure-open" 238 | `isRenderedFrom` 239 | (listStyleType := disclosureOpen) 240 | 241 | "list-style-type:disclosure-closed" 242 | `isRenderedFrom` 243 | (listStyleType := disclosureClosed) 244 | 245 | "list-style-type:cjk-earthly-branch" 246 | `isRenderedFrom` 247 | (listStyleType := cjkEarthlyBranch) 248 | 249 | "list-style-type:cjk-heavenly-stem" 250 | `isRenderedFrom` 251 | (listStyleType := cjkHeavenlyStem) 252 | 253 | describe "list-style-position property" do 254 | 255 | let isRenderedFrom = isRenderedFromInline 256 | 257 | "list-style-position:inherit" 258 | `isRenderedFrom` 259 | (listStylePosition := inherit) 260 | 261 | "list-style-position:initial" 262 | `isRenderedFrom` 263 | (listStylePosition := initial) 264 | 265 | "list-style-position:unset" `isRenderedFrom` (listStylePosition := unset) 266 | 267 | "list-style-position:inside" 268 | `isRenderedFrom` 269 | (listStylePosition := inside) 270 | 271 | "list-style-position:outside" 272 | `isRenderedFrom` 273 | (listStylePosition := outside) 274 | -------------------------------------------------------------------------------- /test/Main.purs: -------------------------------------------------------------------------------- 1 | module Test.Main where 2 | 3 | import Prelude 4 | 5 | import Effect (Effect) 6 | import Effect.Aff (launchAff_) 7 | import Test.AlignSpec as Align 8 | import Test.AnimationsSpec as Animations 9 | import Test.BackgroundsSpec as Backgrounds 10 | import Test.BoxSpec as Box 11 | import Test.ColorSpec as Color 12 | import Test.ContentSpec as Content 13 | import Test.DisplaySpec as Display 14 | import Test.FlexboxSpec as Flexbox 15 | import Test.FontsSpec as Fonts 16 | import Test.GridSpec as Grid 17 | import Test.ImagesSpec as Images 18 | import Test.InlineSpec as Inline 19 | import Test.ListsSpec as Lists 20 | import Test.MaskingSpec as Masking 21 | import Test.MediaQueriesSpec as MediaQueries 22 | import Test.OverflowSpec as Overflow 23 | import Test.PositionSpec as Position 24 | import Test.RenderSpec as Render 25 | import Test.SelectorsSpec as Selectors 26 | import Test.SizingSpec as Sizing 27 | import Test.Spec.Reporter (consoleReporter) 28 | import Test.Spec.Runner (runSpec) 29 | import Test.TextDecorSpec as TextDecor 30 | import Test.TextSpec as Text 31 | import Test.TransformsSpec as Transforms 32 | import Test.TransitionsSpec as Transitions 33 | import Test.UISpec as UI 34 | import Test.UnsafeDeclarationSpec as UnsafeDeclaration 35 | import Test.VisufxSpec as Visufx 36 | import Test.VisurenSpec as Visuren 37 | import Test.WritingModesSpec as WritingModes 38 | 39 | main :: Effect Unit 40 | main = 41 | launchAff_ $ 42 | runSpec [ consoleReporter ] do 43 | Align.spec 44 | Animations.spec 45 | Backgrounds.spec 46 | Box.spec 47 | Color.spec 48 | Content.spec 49 | Display.spec 50 | Flexbox.spec 51 | Fonts.spec 52 | Grid.spec 53 | Images.spec 54 | Inline.spec 55 | Lists.spec 56 | Masking.spec 57 | MediaQueries.spec 58 | Overflow.spec 59 | Position.spec 60 | Render.spec 61 | Selectors.spec 62 | Sizing.spec 63 | Text.spec 64 | TextDecor.spec 65 | Transforms.spec 66 | Transitions.spec 67 | UI.spec 68 | UnsafeDeclaration.spec 69 | Visufx.spec 70 | Visuren.spec 71 | WritingModes.spec 72 | -------------------------------------------------------------------------------- /test/MaskingSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-masking-1/ 2 | 3 | module Test.MaskingSpec where 4 | 5 | import Prelude 6 | 7 | import Color (rgb) 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( inherit 11 | , initial 12 | , linearGradient 13 | , maskImage 14 | , nil 15 | , none 16 | , unset 17 | , url 18 | , (:=) 19 | ) 20 | import Test.Spec (Spec, describe) 21 | import Test.Util (isRenderedFromInline) 22 | 23 | spec :: Spec Unit 24 | spec = do 25 | 26 | let isRenderedFrom = isRenderedFromInline 27 | 28 | describe "Masking Module" do 29 | 30 | describe "mask-image property" do 31 | 32 | "mask-image:inherit" `isRenderedFrom` (maskImage := inherit) 33 | 34 | "mask-image:initial" `isRenderedFrom` (maskImage := initial) 35 | 36 | "mask-image:unset" `isRenderedFrom` (maskImage := unset) 37 | 38 | "mask-image:none" `isRenderedFrom` (maskImage := none) 39 | 40 | "mask-image:url(\"https://example.com/xyz.svg\")" 41 | `isRenderedFrom` 42 | (maskImage := url "https://example.com/xyz.svg") 43 | 44 | "mask-image:linear-gradient(0,#0000ff,#ff0000)" 45 | `isRenderedFrom` 46 | (maskImage := linearGradient nil $ rgb 0 0 255 /\ rgb 255 0 0) 47 | 48 | "mask-image:linear-gradient(0,#0000ff,#ff0000),none,url(\"foo.svg\")" 49 | `isRenderedFrom` 50 | ( maskImage := 51 | linearGradient nil (rgb 0 0 255 /\ rgb 255 0 0) 52 | /\ none 53 | /\ url "foo.svg" 54 | ) 55 | -------------------------------------------------------------------------------- /test/MediaQueriesSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/mediaqueries-3/ 2 | 3 | module Test.MediaQueriesSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( all 9 | , dpcm 10 | , dpi 11 | , landscape 12 | , media 13 | , nil 14 | , portrait 15 | , print 16 | , px 17 | , screen 18 | , universal 19 | , width 20 | , (:/) 21 | , (:=) 22 | , (?) 23 | ) 24 | import Test.Spec (Spec, describe) 25 | import Test.Util (isRenderedFromSheet) 26 | 27 | spec :: Spec Unit 28 | spec = do 29 | 30 | let isRenderedFrom = isRenderedFromSheet 31 | 32 | describe "Media Queries Module" do 33 | 34 | describe "Media types" do 35 | 36 | "@media all{*{width:0}}" 37 | `isRenderedFrom` do 38 | media all {} ? universal ? width := nil 39 | 40 | "@media screen{*{width:0}}" 41 | `isRenderedFrom` do 42 | media screen {} ? universal ? width := nil 43 | 44 | "@media print{*{width:0}}" 45 | `isRenderedFrom` do 46 | media print {} ? universal ? width := nil 47 | 48 | describe "width" do 49 | 50 | "@media all and (width:600px){*{width:0}}" 51 | `isRenderedFrom` do 52 | media all { width: px 600 } ? universal ? width := nil 53 | 54 | "@media all and (max-width:999px) and (min-width:400px){*{width:0}}" 55 | `isRenderedFrom` do 56 | media all { minWidth: px 400, maxWidth: px 999 } ? do 57 | universal ? width := nil 58 | 59 | describe "height" do 60 | 61 | "@media all and (height:600px){*{width:0}}" 62 | `isRenderedFrom` do 63 | media all { height: px 600 } ? universal ? width := nil 64 | 65 | "@media all and (max-height:1600px) and (min-height:800px){*{width:0}}" 66 | `isRenderedFrom` do 67 | media all { minHeight: px 800, maxHeight: px 1600 } ? do 68 | universal ? width := nil 69 | 70 | describe "device-width" do 71 | 72 | "@media all and (device-width:600px){*{width:0}}" 73 | `isRenderedFrom` do 74 | media all { deviceWidth: px 600 } ? universal ? width := nil 75 | 76 | "@media all and (max-device-width:999px) and (min-device-width:400px){*{width:0}}" 77 | `isRenderedFrom` do 78 | media all { minDeviceWidth: px 400, maxDeviceWidth: px 999 } ? do 79 | universal ? width := nil 80 | 81 | describe "device-height" do 82 | 83 | "@media all and (device-height:600px){*{width:0}}" 84 | `isRenderedFrom` do 85 | media all { deviceHeight: px 600 } ? universal ? width := nil 86 | 87 | "@media all and (max-device-height:1600px) and (min-device-height:800px){*{width:0}}" 88 | `isRenderedFrom` do 89 | media all { minDeviceHeight: px 800, maxDeviceHeight: px 1600 } ? do 90 | universal ? width := nil 91 | 92 | describe "orientation" do 93 | 94 | "@media all and (orientation:landscape){*{width:0}}" 95 | `isRenderedFrom` do 96 | media all { orientation: landscape } ? universal ? width := nil 97 | 98 | "@media all and (orientation:portrait){*{width:0}}" 99 | `isRenderedFrom` do 100 | media all { orientation: portrait } ? universal ? width := nil 101 | 102 | describe "aspect-ratio" do 103 | 104 | "@media all and (aspect-ratio:16/9){*{width:0}}" 105 | `isRenderedFrom` do 106 | media all { aspectRatio: 16 :/ 9 } ? universal ? width := nil 107 | 108 | "@media all and (max-aspect-ratio:16/9) and (min-aspect-ratio:4/3){*{width:0}}" 109 | `isRenderedFrom` do 110 | media all { minAspectRatio: 4 :/ 3, maxAspectRatio: 16 :/ 9 } ? do 111 | universal ? width := nil 112 | 113 | describe "device-aspect-ratio" do 114 | 115 | "@media all and (device-aspect-ratio:16/9){*{width:0}}" 116 | `isRenderedFrom` do 117 | media all { deviceAspectRatio: 16 :/ 9 } ? universal ? width := nil 118 | 119 | "@media all and (max-device-aspect-ratio:16/9) and (min-device-aspect-ratio:4/3){*{width:0}}" 120 | `isRenderedFrom` do 121 | media all 122 | { minDeviceAspectRatio: 4 :/ 3, maxDeviceAspectRatio: 16 :/ 9 } ? 123 | do 124 | universal ? width := nil 125 | 126 | describe "color" do 127 | 128 | "@media all and (color:0){*{width:0}}" 129 | `isRenderedFrom` do 130 | media all { color: 0 } ? universal ? width := nil 131 | 132 | "@media all and (max-color:2) and (min-color:0){*{width:0}}" 133 | `isRenderedFrom` do 134 | media all { minColor: 0, maxColor: 2 } ? universal ? width := nil 135 | 136 | describe "color-index" do 137 | 138 | "@media all and (color-index:1){*{width:0}}" 139 | `isRenderedFrom` do 140 | media all { colorIndex: 1 } ? universal ? width := nil 141 | 142 | "@media all and (max-color-index:256) and (min-color-index:1){*{width:0}}" 143 | `isRenderedFrom` do 144 | media all { minColorIndex: 1, maxColorIndex: 256 } ? do 145 | universal ? width := nil 146 | 147 | describe "monochrome" do 148 | 149 | "@media all and (monochrome:0){*{width:0}}" 150 | `isRenderedFrom` do 151 | media all { monochrome: 0 } ? universal ? width := nil 152 | 153 | "@media all and (max-monochrome:2) and (min-monochrome:1){*{width:0}}" 154 | `isRenderedFrom` do 155 | media all { minMonochrome: 1, maxMonochrome: 2 } ? do 156 | universal ? width := nil 157 | 158 | describe "resolution" do 159 | 160 | "@media all and (resolution:300dpi){*{width:0}}" 161 | `isRenderedFrom` do 162 | media all { resolution: dpi 300 } ? universal ? width := nil 163 | 164 | "@media all and (max-resolution:236dpcm) and (min-resolution:118dpcm){*{width:0}}" 165 | `isRenderedFrom` do 166 | media all { minResolution: dpcm 118, maxResolution: dpcm 236 } ? do 167 | universal ? width := nil 168 | -------------------------------------------------------------------------------- /test/OverflowSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-overflow-3/ 2 | 3 | module Test.OverflowSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( auto 9 | , clip 10 | , ellipsis 11 | , hidden 12 | , inherit 13 | , initial 14 | , overflow 15 | , overflowX 16 | , overflowY 17 | , scroll 18 | , textOverflow 19 | , unset 20 | , visible 21 | , (:=) 22 | , (~) 23 | ) 24 | import Test.Spec (Spec, describe) 25 | import Test.Util (isRenderedFromInline) 26 | 27 | spec :: Spec Unit 28 | spec = do 29 | 30 | let isRenderedFrom = isRenderedFromInline 31 | 32 | describe "Overflow Module" do 33 | 34 | describe "overflow-x property" do 35 | 36 | "overflow-x:inherit" `isRenderedFrom` (overflowX := inherit) 37 | 38 | "overflow-x:initial" `isRenderedFrom` (overflowX := initial) 39 | 40 | "overflow-x:unset" `isRenderedFrom` (overflowX := unset) 41 | 42 | "overflow-x:visible" `isRenderedFrom` (overflowX := visible) 43 | 44 | "overflow-x:hidden" `isRenderedFrom` (overflowX := hidden) 45 | 46 | "overflow-x:clip" `isRenderedFrom` (overflowX := clip) 47 | 48 | "overflow-x:scroll" `isRenderedFrom` (overflowX := scroll) 49 | 50 | "overflow-x:auto" `isRenderedFrom` (overflowX := auto) 51 | 52 | describe "overflow-y property" do 53 | 54 | "overflow-y:inherit" `isRenderedFrom` (overflowY := inherit) 55 | 56 | "overflow-y:initial" `isRenderedFrom` (overflowY := initial) 57 | 58 | "overflow-y:unset" `isRenderedFrom` (overflowY := unset) 59 | 60 | "overflow-y:visible" `isRenderedFrom` (overflowY := visible) 61 | 62 | "overflow-y:hidden" `isRenderedFrom` (overflowY := hidden) 63 | 64 | "overflow-y:clip" `isRenderedFrom` (overflowY := clip) 65 | 66 | "overflow-y:scroll" `isRenderedFrom` (overflowY := scroll) 67 | 68 | "overflow-y:auto" `isRenderedFrom` (overflowY := auto) 69 | 70 | describe "overflow property" do 71 | 72 | "overflow:inherit" `isRenderedFrom` (overflow := inherit) 73 | 74 | "overflow:initial" `isRenderedFrom` (overflow := initial) 75 | 76 | "overflow:unset" `isRenderedFrom` (overflow := unset) 77 | 78 | "overflow:visible" `isRenderedFrom` (overflow := visible) 79 | 80 | "overflow:hidden" `isRenderedFrom` (overflow := hidden) 81 | 82 | "overflow:clip" `isRenderedFrom` (overflow := clip) 83 | 84 | "overflow:scroll" `isRenderedFrom` (overflow := scroll) 85 | 86 | "overflow:auto" `isRenderedFrom` (overflow := auto) 87 | 88 | "overflow:visible hidden" `isRenderedFrom` (overflow := visible ~ hidden) 89 | 90 | "overflow:hidden clip" `isRenderedFrom` (overflow := hidden ~ clip) 91 | 92 | "overflow:clip scroll" `isRenderedFrom` (overflow := clip ~ scroll) 93 | 94 | "overflow:scroll auto" `isRenderedFrom` (overflow := scroll ~ auto) 95 | 96 | "overflow:auto visible" `isRenderedFrom` (overflow := auto ~ visible) 97 | 98 | describe "text-overflow property" do 99 | 100 | "text-overflow:inherit" `isRenderedFrom` (textOverflow := inherit) 101 | 102 | "text-overflow:initial" `isRenderedFrom` (textOverflow := initial) 103 | 104 | "text-overflow:unset" `isRenderedFrom` (textOverflow := unset) 105 | 106 | "text-overflow:clip" `isRenderedFrom` (textOverflow := clip) 107 | 108 | "text-overflow:ellipsis" `isRenderedFrom` (textOverflow := ellipsis) 109 | -------------------------------------------------------------------------------- /test/PositionSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-position-3/ 2 | 3 | module Test.PositionSpec where 4 | 5 | import Prelude hiding (bottom, top) 6 | 7 | import Tecton 8 | ( absolute 9 | , auto 10 | , bottom 11 | , fixed 12 | , inherit 13 | , initial 14 | , inset 15 | , insetBlock 16 | , insetBlockEnd 17 | , insetBlockStart 18 | , insetInline 19 | , insetInlineEnd 20 | , insetInlineStart 21 | , left 22 | , nil 23 | , pct 24 | , position 25 | , px 26 | , relative 27 | , right 28 | , static 29 | , sticky 30 | , top 31 | , unset 32 | , (:=) 33 | , (~) 34 | ) 35 | import Test.Spec (Spec, describe) 36 | import Test.Util (isRenderedFromInline) 37 | 38 | spec :: Spec Unit 39 | spec = do 40 | 41 | let isRenderedFrom = isRenderedFromInline 42 | 43 | describe "Positioned Layout Module" do 44 | 45 | describe "position property" do 46 | 47 | "position:inherit" `isRenderedFrom` (position := inherit) 48 | 49 | "position:initial" `isRenderedFrom` (position := initial) 50 | 51 | "position:unset" `isRenderedFrom` (position := unset) 52 | 53 | "position:static" `isRenderedFrom` (position := static) 54 | 55 | "position:relative" `isRenderedFrom` (position := relative) 56 | 57 | "position:absolute" `isRenderedFrom` (position := absolute) 58 | 59 | "position:sticky" `isRenderedFrom` (position := sticky) 60 | 61 | "position:fixed" `isRenderedFrom` (position := fixed) 62 | 63 | describe "top property" do 64 | 65 | "top:inherit" `isRenderedFrom` (top := inherit) 66 | 67 | "top:initial" `isRenderedFrom` (top := initial) 68 | 69 | "top:unset" `isRenderedFrom` (top := unset) 70 | 71 | "top:auto" `isRenderedFrom` (top := auto) 72 | 73 | "top:0" `isRenderedFrom` (top := nil) 74 | 75 | "top:8px" `isRenderedFrom` (top := px 8) 76 | 77 | "top:10%" `isRenderedFrom` (top := pct 10) 78 | 79 | describe "right property" do 80 | 81 | "right:inherit" `isRenderedFrom` (right := inherit) 82 | 83 | "right:initial" `isRenderedFrom` (right := initial) 84 | 85 | "right:unset" `isRenderedFrom` (right := unset) 86 | 87 | "right:auto" `isRenderedFrom` (right := auto) 88 | 89 | "right:0" `isRenderedFrom` (right := nil) 90 | 91 | "right:8px" `isRenderedFrom` (right := px 8) 92 | 93 | "right:10%" `isRenderedFrom` (right := pct 10) 94 | 95 | describe "bottom property" do 96 | 97 | "bottom:inherit" `isRenderedFrom` (bottom := inherit) 98 | 99 | "bottom:initial" `isRenderedFrom` (bottom := initial) 100 | 101 | "bottom:unset" `isRenderedFrom` (bottom := unset) 102 | 103 | "bottom:auto" `isRenderedFrom` (bottom := auto) 104 | 105 | "bottom:0" `isRenderedFrom` (bottom := nil) 106 | 107 | "bottom:8px" `isRenderedFrom` (bottom := px 8) 108 | 109 | "bottom:10%" `isRenderedFrom` (bottom := pct 10) 110 | 111 | describe "left property" do 112 | 113 | "left:inherit" `isRenderedFrom` (left := inherit) 114 | 115 | "left:initial" `isRenderedFrom` (left := initial) 116 | 117 | "left:unset" `isRenderedFrom` (left := unset) 118 | 119 | "left:auto" `isRenderedFrom` (left := auto) 120 | 121 | "left:0" `isRenderedFrom` (left := nil) 122 | 123 | "left:8px" `isRenderedFrom` (left := px 8) 124 | 125 | "left:10%" `isRenderedFrom` (left := pct 10) 126 | 127 | describe "inset-block-start property" do 128 | 129 | "inset-block-start:inherit" `isRenderedFrom` (insetBlockStart := inherit) 130 | 131 | "inset-block-start:initial" `isRenderedFrom` (insetBlockStart := initial) 132 | 133 | "inset-block-start:unset" `isRenderedFrom` (insetBlockStart := unset) 134 | 135 | "inset-block-start:auto" `isRenderedFrom` (insetBlockStart := auto) 136 | 137 | "inset-block-start:0" `isRenderedFrom` (insetBlockStart := nil) 138 | 139 | "inset-block-start:8px" `isRenderedFrom` (insetBlockStart := px 8) 140 | 141 | "inset-block-start:10%" `isRenderedFrom` (insetBlockStart := pct 10) 142 | 143 | describe "inset-inline-start property" do 144 | 145 | "inset-inline-start:inherit" 146 | `isRenderedFrom` 147 | (insetInlineStart := inherit) 148 | 149 | "inset-inline-start:initial" 150 | `isRenderedFrom` 151 | (insetInlineStart := initial) 152 | 153 | "inset-inline-start:unset" `isRenderedFrom` (insetInlineStart := unset) 154 | 155 | "inset-inline-start:auto" `isRenderedFrom` (insetInlineStart := auto) 156 | 157 | "inset-inline-start:0" `isRenderedFrom` (insetInlineStart := nil) 158 | 159 | "inset-inline-start:8px" `isRenderedFrom` (insetInlineStart := px 8) 160 | 161 | "inset-inline-start:10%" `isRenderedFrom` (insetInlineStart := pct 10) 162 | 163 | describe "inset-block-end property" do 164 | 165 | "inset-block-end:inherit" `isRenderedFrom` (insetBlockEnd := inherit) 166 | 167 | "inset-block-end:initial" `isRenderedFrom` (insetBlockEnd := initial) 168 | 169 | "inset-block-end:unset" `isRenderedFrom` (insetBlockEnd := unset) 170 | 171 | "inset-block-end:auto" `isRenderedFrom` (insetBlockEnd := auto) 172 | 173 | "inset-block-end:0" `isRenderedFrom` (insetBlockEnd := nil) 174 | 175 | "inset-block-end:8px" `isRenderedFrom` (insetBlockEnd := px 8) 176 | 177 | "inset-block-end:10%" `isRenderedFrom` (insetBlockEnd := pct 10) 178 | 179 | describe "inset-inline-end property" do 180 | 181 | "inset-inline-end:inherit" `isRenderedFrom` (insetInlineEnd := inherit) 182 | 183 | "inset-inline-end:initial" `isRenderedFrom` (insetInlineEnd := initial) 184 | 185 | "inset-inline-end:unset" `isRenderedFrom` (insetInlineEnd := unset) 186 | 187 | "inset-inline-end:auto" `isRenderedFrom` (insetInlineEnd := auto) 188 | 189 | "inset-inline-end:0" `isRenderedFrom` (insetInlineEnd := nil) 190 | 191 | "inset-inline-end:8px" `isRenderedFrom` (insetInlineEnd := px 8) 192 | 193 | "inset-inline-end:10%" `isRenderedFrom` (insetInlineEnd := pct 10) 194 | 195 | describe "inset-block" do 196 | 197 | "inset-block:inherit" `isRenderedFrom` (insetBlock := inherit) 198 | 199 | "inset-block:initial" `isRenderedFrom` (insetBlock := initial) 200 | 201 | "inset-block:unset" `isRenderedFrom` (insetBlock := unset) 202 | 203 | "inset-block:auto" `isRenderedFrom` (insetBlock := auto) 204 | 205 | "inset-block:10px" `isRenderedFrom` (insetBlock := px 10) 206 | 207 | "inset-block:1%" `isRenderedFrom` (insetBlock := pct 1) 208 | 209 | "inset-block:auto 1px" `isRenderedFrom` (insetBlock := auto ~ px 1) 210 | 211 | "inset-block:10px 1%" `isRenderedFrom` (insetBlock := px 10 ~ pct 1) 212 | 213 | "inset-block:10% auto" `isRenderedFrom` (insetBlock := pct 10 ~ auto) 214 | 215 | "inset-block:1% 10px" `isRenderedFrom` (insetBlock := pct 1 ~ px 10) 216 | 217 | describe "inset-inline" do 218 | 219 | "inset-inline:inherit" `isRenderedFrom` (insetInline := inherit) 220 | 221 | "inset-inline:initial" `isRenderedFrom` (insetInline := initial) 222 | 223 | "inset-inline:unset" `isRenderedFrom` (insetInline := unset) 224 | 225 | "inset-inline:auto" `isRenderedFrom` (insetInline := auto) 226 | 227 | "inset-inline:10px" `isRenderedFrom` (insetInline := px 10) 228 | 229 | "inset-inline:1%" `isRenderedFrom` (insetInline := pct 1) 230 | 231 | "inset-inline:auto 1px" `isRenderedFrom` (insetInline := auto ~ px 1) 232 | 233 | "inset-inline:10px 1%" `isRenderedFrom` (insetInline := px 10 ~ pct 1) 234 | 235 | "inset-inline:10% auto" `isRenderedFrom` (insetInline := pct 10 ~ auto) 236 | 237 | "inset-inline:1% 10px" `isRenderedFrom` (insetInline := pct 1 ~ px 10) 238 | 239 | describe "inset" do 240 | 241 | "inset:inherit" `isRenderedFrom` (inset := inherit) 242 | 243 | "inset:initial" `isRenderedFrom` (inset := initial) 244 | 245 | "inset:unset" `isRenderedFrom` (inset := unset) 246 | 247 | "inset:auto" `isRenderedFrom` (inset := auto) 248 | 249 | "inset:10px" `isRenderedFrom` (inset := px 10) 250 | 251 | "inset:1%" `isRenderedFrom` (inset := pct 1) 252 | 253 | "inset:1% 1px" `isRenderedFrom` (inset := pct 1 ~ px 1) 254 | 255 | "inset:1px auto" `isRenderedFrom` (inset := px 1 ~ auto) 256 | 257 | "inset:auto 1%" `isRenderedFrom` (inset := auto ~ pct 1) 258 | 259 | "inset:1% 1px auto" `isRenderedFrom` (inset := pct 1 ~ px 1 ~ auto) 260 | 261 | "inset:1px auto 1%" `isRenderedFrom` (inset := px 1 ~ auto ~ pct 1) 262 | 263 | "inset:auto 1% 1px" `isRenderedFrom` (inset := auto ~ pct 1 ~ px 1) 264 | 265 | "inset:1% 1px auto 2%" 266 | `isRenderedFrom` 267 | (inset := pct 1 ~ px 1 ~ auto ~ pct 2) 268 | 269 | "inset:2% 1% 1px auto" 270 | `isRenderedFrom` 271 | (inset := pct 2 ~ pct 1 ~ px 1 ~ auto) 272 | 273 | "inset:auto 2% 1% 1px" 274 | `isRenderedFrom` 275 | (inset := auto ~ pct 2 ~ pct 1 ~ px 1) 276 | 277 | "inset:1px auto 2% 1%" 278 | `isRenderedFrom` 279 | (inset := px 1 ~ auto ~ pct 2 ~ pct 1) 280 | -------------------------------------------------------------------------------- /test/RenderSpec.purs: -------------------------------------------------------------------------------- 1 | module Test.RenderSpec where 2 | 3 | import Prelude 4 | 5 | import Tecton 6 | ( all 7 | , compact 8 | , height 9 | , media 10 | , nil 11 | , pretty 12 | , renderInline 13 | , renderSheet 14 | , universal 15 | , width 16 | , (:=) 17 | , (?) 18 | ) 19 | import Tecton.Rule as Rule 20 | import Test.Spec (Spec, describe, it) 21 | import Test.Spec.Assertions (shouldEqual) 22 | 23 | spec :: Spec Unit 24 | spec = do 25 | 26 | describe "renderInline" do 27 | it "renders a series of declarations" $ 28 | let 29 | inlineStyles = 30 | renderInline Rule.do 31 | width := nil 32 | height := nil 33 | in 34 | inlineStyles `shouldEqual` "width: 0; height: 0" 35 | 36 | describe "renderSheet" do 37 | it "renders a compact style sheet" $ 38 | let 39 | actual = renderSheet compact $ media all {} ? universal ? width := nil 40 | expected = "@media all{*{width:0}}" 41 | in 42 | actual `shouldEqual` expected 43 | it "renders a pretty-printed style sheet" $ 44 | let 45 | actual = renderSheet pretty $ media all {} ? universal ? width := nil 46 | expected = 47 | """@media all { 48 | * { 49 | width: 0; 50 | } 51 | }""" 52 | in 53 | actual `shouldEqual` expected 54 | -------------------------------------------------------------------------------- /test/SelectorsSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/selectors-3/ 2 | -- https://www.w3.org/TR/selectors-4/ 3 | -- https://www.w3.org/TR/css-pseudo-4/ 4 | 5 | module Test.SelectorsSpec where 6 | 7 | import Prelude hiding (not) 8 | 9 | import Color (rgb) 10 | import Data.Tuple.Nested ((/\)) 11 | import Tecton 12 | ( AttrName(..) 13 | , ClassName(..) 14 | , ElementId(..) 15 | , PseudoClass(..) 16 | , PseudoElement(..) 17 | , a 18 | , active 19 | , after 20 | , backgroundColor 21 | , before 22 | , checked 23 | , disabled 24 | , empty 25 | , enabled 26 | , even 27 | , firstChild 28 | , firstLetter 29 | , firstLine 30 | , firstOfType 31 | , focus 32 | , focusWithin 33 | , hover 34 | , href 35 | , hreflang 36 | , indeterminate 37 | , lang 38 | , lastChild 39 | , lastOfType 40 | , link 41 | , nil 42 | , not 43 | , nthChild 44 | , nthLastChild 45 | , nthOfType 46 | , odd 47 | , onlyChild 48 | , onlyOfType 49 | , placeholder 50 | , root 51 | , selection 52 | , span 53 | , target 54 | , title 55 | , universal 56 | , visited 57 | , width 58 | , (#+) 59 | , (#-) 60 | , ($=) 61 | , (&#) 62 | , (&.) 63 | , (&:) 64 | , (&::) 65 | , (&@) 66 | , (*=) 67 | , (:=) 68 | , (?) 69 | , (@=) 70 | , (^=) 71 | , (|*) 72 | , (|+) 73 | , (|=) 74 | , (|>) 75 | , (|~) 76 | , (~=) 77 | ) 78 | import Test.Spec (Spec, describe) 79 | import Test.Util (isRenderedFromSheet) 80 | 81 | spec :: Spec Unit 82 | spec = do 83 | 84 | let isRenderedFrom = isRenderedFromSheet 85 | 86 | describe "Selectors Module" do 87 | 88 | describe "Universal selector" do 89 | 90 | "*{width:0}" `isRenderedFrom` do universal ? width := nil 91 | 92 | describe "Element selectors" do 93 | 94 | "a{width:0}" `isRenderedFrom` do a ? width := nil 95 | 96 | "span{width:0}" `isRenderedFrom` do span ? width := nil 97 | 98 | describe "Attribute selectors" do 99 | 100 | "*[href]{width:0}" 101 | `isRenderedFrom` do 102 | universal &@ href ? width := nil 103 | 104 | "*[href=\"http://www.w3.org/\"]{width:0}" 105 | `isRenderedFrom` do 106 | universal &@ href @= "http://www.w3.org/" ? width := nil 107 | 108 | "*[data-states~=\"selected\"]{width:0}" 109 | `isRenderedFrom` do 110 | universal &@ AttrName "data-states" ~= "selected" ? width := nil 111 | 112 | "*[hreflang|=\"en\"]{width:0}" 113 | `isRenderedFrom` do 114 | universal &@ hreflang |= "en" ? width := nil 115 | 116 | "*[data-timezone^=\"UTC-\"]{width:0}" 117 | `isRenderedFrom` do 118 | universal &@ AttrName "data-timezone" ^= "UTC-" ? width := nil 119 | 120 | "*[data-timezone$=\":30\"]{width:0}" 121 | `isRenderedFrom` do 122 | universal &@ AttrName "data-timezone" $= ":30" ? width := nil 123 | 124 | "*[title*=\"hello\"]{width:0}" 125 | `isRenderedFrom` do 126 | universal &@ title *= "hello" ? width := nil 127 | 128 | describe "Class selectors" do 129 | 130 | "*.pastoral{width:0}" 131 | `isRenderedFrom` do 132 | universal &. ClassName "pastoral" ? width := nil 133 | 134 | "*.pastoral.marine{width:0}" 135 | `isRenderedFrom` do 136 | universal &. ClassName "pastoral" &. ClassName "marine" ? width := nil 137 | 138 | describe "ID selectors" do 139 | 140 | "*#chapter1{width:0}" 141 | `isRenderedFrom` do 142 | universal &# ElementId "chapter1" ? width := nil 143 | 144 | "*#z98y{width:0}" `isRenderedFrom` do 145 | universal &# ElementId "z98y" ? width := nil 146 | 147 | describe "Pseudo-classes" do 148 | 149 | "*:link{width:0}" `isRenderedFrom` do universal &: link ? width := nil 150 | 151 | "*:visited{width:0}" 152 | `isRenderedFrom` do 153 | universal &: visited ? width := nil 154 | 155 | "*:hover{width:0}" `isRenderedFrom` do universal &: hover ? width := nil 156 | 157 | "*:focus{width:0}" `isRenderedFrom` do universal &: focus ? width := nil 158 | 159 | "*:active{width:0}" `isRenderedFrom` do universal &: active ? width := nil 160 | 161 | "*:target{width:0}" `isRenderedFrom` do universal &: target ? width := nil 162 | 163 | "*:lang(en-US){width:0}" 164 | `isRenderedFrom` do 165 | universal &: lang "en-US" ? width := nil 166 | 167 | "*:enabled{width:0}" 168 | `isRenderedFrom` do 169 | universal &: enabled ? width := nil 170 | 171 | "*:disabled{width:0}" 172 | `isRenderedFrom` do 173 | universal &: disabled ? width := nil 174 | 175 | "*:checked{width:0}" 176 | `isRenderedFrom` do 177 | universal &: checked ? width := nil 178 | 179 | "*:indeterminate{width:0}" 180 | `isRenderedFrom` do 181 | universal &: indeterminate ? width := nil 182 | 183 | "*:root{width:0}" `isRenderedFrom` do universal &: root ? width := nil 184 | 185 | "*:nth-child(2n){width:0}" 186 | `isRenderedFrom` do 187 | universal &: nthChild even ? width := nil 188 | 189 | "*:nth-child(2n+1){width:0}" 190 | `isRenderedFrom` do 191 | universal &: nthChild odd ? width := nil 192 | 193 | "*:nth-child(2n){width:0}" 194 | `isRenderedFrom` do 195 | universal &: nthChild (2 #+ 0) ? width := nil 196 | 197 | "*:nth-child(2n+1){width:0}" 198 | `isRenderedFrom` do 199 | universal &: nthChild (2 #+ 1) ? width := nil 200 | 201 | "*:nth-child(10n-1){width:0}" 202 | `isRenderedFrom` do 203 | universal &: nthChild (10 #- 1) ? width := nil 204 | 205 | "*:nth-last-child(-n+2){width:0}" 206 | `isRenderedFrom` do 207 | universal &: nthLastChild ((-1) #+ 2) ? width := nil 208 | 209 | "*:nth-last-child(2n+1){width:0}" 210 | `isRenderedFrom` do 211 | universal &: nthLastChild odd ? width := nil 212 | 213 | "*:nth-of-type(2n+1){width:0}" 214 | `isRenderedFrom` do 215 | universal &: nthOfType (2 #+ 1) ? width := nil 216 | 217 | "*:nth-of-type(2n){width:0}" 218 | `isRenderedFrom` do 219 | universal &: nthOfType (2 #+ 0) ? width := nil 220 | 221 | "*:first-child{width:0}" 222 | `isRenderedFrom` do 223 | universal &: firstChild ? width := nil 224 | 225 | "*:last-child{width:0}" 226 | `isRenderedFrom` do 227 | universal &: lastChild ? width := nil 228 | 229 | "*:first-of-type{width:0}" 230 | `isRenderedFrom` do 231 | universal &: firstOfType ? width := nil 232 | 233 | "*:last-of-type{width:0}" 234 | `isRenderedFrom` do 235 | universal &: lastOfType ? width := nil 236 | 237 | "*:only-child{width:0}" 238 | `isRenderedFrom` do 239 | universal &: onlyChild ? width := nil 240 | 241 | "*:only-of-type{width:0}" 242 | `isRenderedFrom` do 243 | universal &: onlyOfType ? width := nil 244 | 245 | "*:empty{width:0}" `isRenderedFrom` do universal &: empty ? width := nil 246 | 247 | "*:not(*){width:0}" 248 | `isRenderedFrom` do 249 | universal &: not universal ? width := nil 250 | 251 | "*:not(*.foo~*:checked){width:0}" 252 | `isRenderedFrom` do 253 | universal 254 | &: not (universal &. ClassName "foo" |~ universal &: checked) 255 | ? do 256 | width := nil 257 | 258 | "*:not(*,*){width:0}" 259 | `isRenderedFrom` do 260 | universal &: not (universal /\ universal) ? width := nil 261 | 262 | "*:focus-within{width:0}" 263 | `isRenderedFrom` do 264 | universal &: focusWithin ? width := nil 265 | 266 | "*:-moz-user-disabled{background-color:#ff0000}" 267 | `isRenderedFrom` do 268 | universal &: PseudoClass "-moz-user-disabled" 269 | ? backgroundColor 270 | := rgb 255 0 0 271 | 272 | describe "Pseudo-elements" do 273 | 274 | "*::first-line{width:0}" 275 | `isRenderedFrom` do 276 | universal &:: firstLine ? width := nil 277 | 278 | "*::first-letter{width:0}" 279 | `isRenderedFrom` do 280 | universal &:: firstLetter ? width := nil 281 | 282 | "*::before{width:0}" 283 | `isRenderedFrom` do 284 | universal &:: before ? width := nil 285 | 286 | "*::after{width:0}" 287 | `isRenderedFrom` do 288 | universal &:: after ? width := nil 289 | 290 | "*::placeholder{width:0}" 291 | `isRenderedFrom` do 292 | universal &:: placeholder ? width := nil 293 | 294 | "*::selection{width:0}" 295 | `isRenderedFrom` do 296 | universal &:: selection ? width := nil 297 | 298 | "*::-webkit-meter-bar{background-color:#eeeeee}" 299 | `isRenderedFrom` do 300 | universal &:: PseudoElement "-webkit-meter-bar" ? backgroundColor := 301 | rgb 238 238 238 302 | 303 | describe "Combinators" do 304 | 305 | "* *{width:0}" `isRenderedFrom` do universal |* universal ? width := nil 306 | 307 | "*>*{width:0}" `isRenderedFrom` do universal |> universal ? width := nil 308 | 309 | "*+*{width:0}" `isRenderedFrom` do universal |+ universal ? width := nil 310 | 311 | "*~*{width:0}" `isRenderedFrom` do universal |~ universal ? width := nil 312 | 313 | describe "Groups of selectors" do 314 | 315 | "*:checked,*.checked{width:0}" 316 | `isRenderedFrom` do 317 | universal &: checked /\ universal &. ClassName "checked" ? width := 318 | nil 319 | 320 | "*.foo,*::after{width:0}" 321 | `isRenderedFrom` do 322 | universal &. ClassName "foo" /\ universal &:: after ? width := nil 323 | -------------------------------------------------------------------------------- /test/SizingSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-sizing-3/ 2 | 3 | module Test.SizingSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( auto 9 | , borderBox 10 | , boxSizing 11 | , contentBox 12 | , fitContent 13 | , height 14 | , inherit 15 | , initial 16 | , maxContent 17 | , maxHeight 18 | , maxWidth 19 | , minContent 20 | , minHeight 21 | , minWidth 22 | , none 23 | , pct 24 | , px 25 | , unset 26 | , width 27 | , (:=) 28 | , (@+@) 29 | ) 30 | import Test.Spec (Spec, describe) 31 | import Test.Util (isRenderedFromInline) 32 | 33 | spec :: Spec Unit 34 | spec = do 35 | 36 | let isRenderedFrom = isRenderedFromInline 37 | 38 | describe "Box Sizing Module" do 39 | 40 | describe "width property" do 41 | 42 | "width:inherit" `isRenderedFrom` (width := inherit) 43 | 44 | "width:initial" `isRenderedFrom` (width := initial) 45 | 46 | "width:unset" `isRenderedFrom` (width := unset) 47 | 48 | "width:auto" `isRenderedFrom` (width := auto) 49 | 50 | "width:100px" `isRenderedFrom` (width := px 100) 51 | 52 | "width:50%" `isRenderedFrom` (width := pct 50) 53 | 54 | "width:calc(100px + 50%)" `isRenderedFrom` (width := px 100 @+@ pct 50) 55 | 56 | "width:min-content" `isRenderedFrom` (width := minContent) 57 | 58 | "width:max-content" `isRenderedFrom` (width := maxContent) 59 | 60 | "width:fit-content(100px)" `isRenderedFrom` (width := fitContent (px 100)) 61 | 62 | "width:fit-content(50%)" `isRenderedFrom` (width := fitContent (pct 50)) 63 | 64 | "width:fit-content(calc(100px + 50%))" 65 | `isRenderedFrom` 66 | (width := fitContent (px 100 @+@ pct 50)) 67 | 68 | describe "height property" do 69 | 70 | "height:inherit" `isRenderedFrom` (height := inherit) 71 | 72 | "height:initial" `isRenderedFrom` (height := initial) 73 | 74 | "height:unset" `isRenderedFrom` (height := unset) 75 | 76 | "height:auto" `isRenderedFrom` (height := auto) 77 | 78 | "height:100px" `isRenderedFrom` (height := px 100) 79 | 80 | "height:50%" `isRenderedFrom` (height := pct 50) 81 | 82 | "height:calc(100px + 50%)" `isRenderedFrom` (height := px 100 @+@ pct 50) 83 | 84 | "height:min-content" `isRenderedFrom` (height := minContent) 85 | 86 | "height:max-content" `isRenderedFrom` (height := maxContent) 87 | 88 | "height:fit-content(100px)" `isRenderedFrom` 89 | (height := fitContent (px 100)) 90 | 91 | "height:fit-content(50%)" `isRenderedFrom` (height := fitContent (pct 50)) 92 | 93 | "height:fit-content(calc(100px + 50%))" 94 | `isRenderedFrom` 95 | (height := fitContent (px 100 @+@ pct 50)) 96 | 97 | describe "min-width property" do 98 | 99 | "min-width:inherit" `isRenderedFrom` (minWidth := inherit) 100 | 101 | "min-width:initial" `isRenderedFrom` (minWidth := initial) 102 | 103 | "min-width:unset" `isRenderedFrom` (minWidth := unset) 104 | 105 | "min-width:auto" `isRenderedFrom` (minWidth := auto) 106 | 107 | "min-width:100px" `isRenderedFrom` (minWidth := px 100) 108 | 109 | "min-width:50%" `isRenderedFrom` (minWidth := pct 50) 110 | 111 | "min-width:calc(100px + 50%)" 112 | `isRenderedFrom` 113 | (minWidth := px 100 @+@ pct 50) 114 | 115 | "min-width:min-content" `isRenderedFrom` (minWidth := minContent) 116 | 117 | "min-width:max-content" `isRenderedFrom` (minWidth := maxContent) 118 | 119 | "min-width:fit-content(100px)" 120 | `isRenderedFrom` 121 | (minWidth := fitContent (px 100)) 122 | 123 | "min-width:fit-content(50%)" 124 | `isRenderedFrom` 125 | (minWidth := fitContent (pct 50)) 126 | 127 | "min-width:fit-content(calc(100px + 50%))" 128 | `isRenderedFrom` 129 | (minWidth := fitContent (px 100 @+@ pct 50)) 130 | 131 | describe "min-height property" do 132 | 133 | "min-height:inherit" `isRenderedFrom` (minHeight := inherit) 134 | 135 | "min-height:initial" `isRenderedFrom` (minHeight := initial) 136 | 137 | "min-height:unset" `isRenderedFrom` (minHeight := unset) 138 | 139 | "min-height:auto" `isRenderedFrom` (minHeight := auto) 140 | 141 | "min-height:100px" `isRenderedFrom` (minHeight := px 100) 142 | 143 | "min-height:50%" `isRenderedFrom` (minHeight := pct 50) 144 | 145 | "min-height:calc(100px + 50%)" 146 | `isRenderedFrom` 147 | (minHeight := px 100 @+@ pct 50) 148 | 149 | "min-height:min-content" `isRenderedFrom` (minHeight := minContent) 150 | 151 | "min-height:max-content" `isRenderedFrom` (minHeight := maxContent) 152 | 153 | "min-height:fit-content(100px)" 154 | `isRenderedFrom` 155 | (minHeight := fitContent (px 100)) 156 | 157 | "min-height:fit-content(50%)" 158 | `isRenderedFrom` 159 | (minHeight := fitContent (pct 50)) 160 | 161 | "min-height:fit-content(calc(100px + 50%))" 162 | `isRenderedFrom` 163 | (minHeight := fitContent (px 100 @+@ pct 50)) 164 | 165 | describe "max-width property" do 166 | 167 | "max-width:inherit" `isRenderedFrom` (maxWidth := inherit) 168 | 169 | "max-width:initial" `isRenderedFrom` (maxWidth := initial) 170 | 171 | "max-width:unset" `isRenderedFrom` (maxWidth := unset) 172 | 173 | "max-width:none" `isRenderedFrom` (maxWidth := none) 174 | 175 | "max-width:100px" `isRenderedFrom` (maxWidth := px 100) 176 | 177 | "max-width:50%" `isRenderedFrom` (maxWidth := pct 50) 178 | 179 | "max-width:calc(100px + 50%)" 180 | `isRenderedFrom` 181 | (maxWidth := px 100 @+@ pct 50) 182 | 183 | "max-width:max-content" `isRenderedFrom` (maxWidth := maxContent) 184 | 185 | "max-width:max-content" `isRenderedFrom` (maxWidth := maxContent) 186 | 187 | "max-width:fit-content(100px)" 188 | `isRenderedFrom` 189 | (maxWidth := fitContent (px 100)) 190 | 191 | "max-width:fit-content(50%)" 192 | `isRenderedFrom` 193 | (maxWidth := fitContent (pct 50)) 194 | 195 | "max-width:fit-content(calc(100px + 50%))" 196 | `isRenderedFrom` 197 | (maxWidth := fitContent (px 100 @+@ pct 50)) 198 | 199 | describe "max-height property" do 200 | 201 | "max-height:inherit" `isRenderedFrom` (maxHeight := inherit) 202 | 203 | "max-height:initial" `isRenderedFrom` (maxHeight := initial) 204 | 205 | "max-height:unset" `isRenderedFrom` (maxHeight := unset) 206 | 207 | "max-height:none" `isRenderedFrom` (maxHeight := none) 208 | 209 | "max-height:100px" `isRenderedFrom` (maxHeight := px 100) 210 | 211 | "max-height:50%" `isRenderedFrom` (maxHeight := pct 50) 212 | 213 | "max-height:calc(100px + 50%)" 214 | `isRenderedFrom` 215 | (maxHeight := px 100 @+@ pct 50) 216 | 217 | "max-height:max-content" `isRenderedFrom` (maxHeight := maxContent) 218 | 219 | "max-height:max-content" `isRenderedFrom` (maxHeight := maxContent) 220 | 221 | "max-height:fit-content(100px)" 222 | `isRenderedFrom` 223 | (maxHeight := fitContent (px 100)) 224 | 225 | "max-height:fit-content(50%)" 226 | `isRenderedFrom` 227 | (maxHeight := fitContent (pct 50)) 228 | 229 | "max-height:fit-content(calc(100px + 50%))" 230 | `isRenderedFrom` 231 | (maxHeight := fitContent (px 100 @+@ pct 50)) 232 | 233 | describe "box-sizing property" do 234 | 235 | "box-sizing:inherit" `isRenderedFrom` (boxSizing := inherit) 236 | 237 | "box-sizing:initial" `isRenderedFrom` (boxSizing := initial) 238 | 239 | "box-sizing:unset" `isRenderedFrom` (boxSizing := unset) 240 | 241 | "box-sizing:content-box" `isRenderedFrom` (boxSizing := contentBox) 242 | 243 | "box-sizing:border-box" `isRenderedFrom` (boxSizing := borderBox) -------------------------------------------------------------------------------- /test/TextDecorSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-text-decor-3/ 2 | 3 | module Test.TextDecorSpec where 4 | 5 | import Prelude 6 | 7 | import Color (rgb) 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( blink 11 | , dashed 12 | , dotted 13 | , double 14 | , em 15 | , inherit 16 | , initial 17 | , lineThrough 18 | , nil 19 | , none 20 | , overline 21 | , px 22 | , solid 23 | , textDecorationColor 24 | , textDecorationLine 25 | , textDecorationStyle 26 | , textShadow 27 | , underline 28 | , unset 29 | , wavy 30 | , (:=) 31 | , (~) 32 | ) 33 | import Test.Spec (Spec, describe) 34 | import Test.Util (isRenderedFromInline) 35 | 36 | spec :: Spec Unit 37 | spec = do 38 | 39 | let isRenderedFrom = isRenderedFromInline 40 | 41 | describe "Text Decoration Module" do 42 | 43 | describe "text-decoration-line" do 44 | 45 | "text-decoration-line:inherit" 46 | `isRenderedFrom` 47 | (textDecorationLine := inherit) 48 | 49 | "text-decoration-line:initial" 50 | `isRenderedFrom` 51 | (textDecorationLine := initial) 52 | 53 | "text-decoration-line:unset" 54 | `isRenderedFrom` 55 | (textDecorationLine := unset) 56 | 57 | "text-decoration-line:none" 58 | `isRenderedFrom` 59 | (textDecorationLine := none) 60 | 61 | "text-decoration-line:underline overline line-through blink" 62 | `isRenderedFrom` 63 | (textDecorationLine := underline ~ overline ~ lineThrough ~ blink) 64 | 65 | "text-decoration-line:overline line-through blink" 66 | `isRenderedFrom` 67 | (textDecorationLine := overline ~ lineThrough ~ blink) 68 | 69 | "text-decoration-line:underline line-through blink" 70 | `isRenderedFrom` 71 | (textDecorationLine := underline ~ lineThrough ~ blink) 72 | 73 | "text-decoration-line:underline overline blink" 74 | `isRenderedFrom` 75 | (textDecorationLine := underline ~ overline ~ blink) 76 | 77 | "text-decoration-line:underline overline line-through" 78 | `isRenderedFrom` 79 | (textDecorationLine := underline ~ overline ~ lineThrough) 80 | 81 | "text-decoration-line:line-through blink" 82 | `isRenderedFrom` 83 | (textDecorationLine := lineThrough ~ blink) 84 | 85 | "text-decoration-line:underline blink" 86 | `isRenderedFrom` 87 | (textDecorationLine := underline ~ blink) 88 | 89 | "text-decoration-line:underline overline" 90 | `isRenderedFrom` 91 | (textDecorationLine := underline ~ overline) 92 | 93 | "text-decoration-line:underline line-through" 94 | `isRenderedFrom` 95 | (textDecorationLine := underline ~ lineThrough) 96 | 97 | "text-decoration-line:overline blink" 98 | `isRenderedFrom` 99 | (textDecorationLine := overline ~ blink) 100 | 101 | "text-decoration-line:underline" 102 | `isRenderedFrom` 103 | (textDecorationLine := underline) 104 | 105 | "text-decoration-line:overline" 106 | `isRenderedFrom` 107 | (textDecorationLine := overline) 108 | 109 | "text-decoration-line:line-through" 110 | `isRenderedFrom` 111 | (textDecorationLine := lineThrough) 112 | 113 | "text-decoration-line:blink" 114 | `isRenderedFrom` 115 | (textDecorationLine := blink) 116 | 117 | describe "text-decoration-style" do 118 | 119 | "text-decoration-style:inherit" 120 | `isRenderedFrom` 121 | (textDecorationStyle := inherit) 122 | 123 | "text-decoration-style:initial" 124 | `isRenderedFrom` 125 | (textDecorationStyle := initial) 126 | 127 | "text-decoration-style:unset" 128 | `isRenderedFrom` 129 | (textDecorationStyle := unset) 130 | 131 | "text-decoration-style:solid" 132 | `isRenderedFrom` 133 | (textDecorationStyle := solid) 134 | 135 | "text-decoration-style:double" 136 | `isRenderedFrom` 137 | (textDecorationStyle := double) 138 | 139 | "text-decoration-style:dotted" 140 | `isRenderedFrom` 141 | (textDecorationStyle := dotted) 142 | 143 | "text-decoration-style:dashed" 144 | `isRenderedFrom` 145 | (textDecorationStyle := dashed) 146 | 147 | "text-decoration-style:wavy" 148 | `isRenderedFrom` 149 | (textDecorationStyle := wavy) 150 | 151 | describe "text-decoration-color property" do 152 | 153 | "text-decoration-color:inherit" 154 | `isRenderedFrom` 155 | (textDecorationColor := inherit) 156 | 157 | "text-decoration-color:initial" 158 | `isRenderedFrom` 159 | (textDecorationColor := initial) 160 | 161 | "text-decoration-color:unset" 162 | `isRenderedFrom` 163 | (textDecorationColor := unset) 164 | 165 | "text-decoration-color:#ff0000" 166 | `isRenderedFrom` 167 | (textDecorationColor := rgb 255 0 0) 168 | 169 | describe "text-shadow property" do 170 | 171 | "text-shadow:inherit" `isRenderedFrom` (textShadow := inherit) 172 | 173 | "text-shadow:initial" `isRenderedFrom` (textShadow := initial) 174 | 175 | "text-shadow:unset" `isRenderedFrom` (textShadow := unset) 176 | 177 | "text-shadow:none" `isRenderedFrom` (textShadow := none) 178 | 179 | "text-shadow:#ffc0cb 1px 1px 2px" 180 | `isRenderedFrom` 181 | (textShadow := rgb 255 192 203 ~ px 1 ~ px 1 ~ px 2) 182 | 183 | "text-shadow:#ffcc00 1px 0 10px" 184 | `isRenderedFrom` 185 | (textShadow := rgb 255 204 0 ~ px 1 ~ nil ~ px 10) 186 | 187 | "text-shadow:#558abb 5px 5px" 188 | `isRenderedFrom` 189 | (textShadow := rgb 85 138 187 ~ px 5 ~ px 5) 190 | 191 | "text-shadow:#ff0000 2px 5px" 192 | `isRenderedFrom` 193 | (textShadow := rgb 255 0 0 ~ px 2 ~ px 5) 194 | 195 | "text-shadow:2px 4px 3px" 196 | `isRenderedFrom` 197 | (textShadow := px 2 ~ px 4 ~ px 3) 198 | 199 | "text-shadow:5px 10px" `isRenderedFrom` (textShadow := px 5 ~ px 10) 200 | 201 | "text-shadow:#ff0000 1px 1px 2px,#0000ff 0 0 1em,#0000ff 0 0 0.2em" 202 | `isRenderedFrom` 203 | let 204 | blue = rgb 0 0 255 205 | in 206 | textShadow := 207 | (rgb 255 0 0 ~ px 1 ~ px 1 ~ px 2) 208 | /\ blue ~ nil ~ nil ~ em 1 209 | /\ blue ~ nil ~ nil ~ em 0.2 210 | -------------------------------------------------------------------------------- /test/TextSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-text-3/ 2 | 3 | module Test.TextSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( breakAll 9 | , breakSpaces 10 | , breakWord 11 | , capitalize 12 | , center 13 | , em 14 | , end 15 | , fullSizeKana 16 | , fullWidth 17 | , inherit 18 | , initial 19 | , justify 20 | , justifyAll 21 | , keepAll 22 | , left 23 | , letterSpacing 24 | , lowercase 25 | , matchParent 26 | , none 27 | , normal 28 | , nowrap 29 | , pct 30 | , pre 31 | , preLine 32 | , preWrap 33 | , px 34 | , right 35 | , start 36 | , textAlign 37 | , textIndent 38 | , textTransform 39 | , unset 40 | , uppercase 41 | , whiteSpace 42 | , wordBreak 43 | , wordSpacing 44 | , (:=) 45 | , (~) 46 | ) 47 | import Test.Spec (Spec, describe) 48 | import Test.Util (isRenderedFromInline) 49 | 50 | spec :: Spec Unit 51 | spec = do 52 | 53 | let isRenderedFrom = isRenderedFromInline 54 | 55 | describe "Text Module" do 56 | 57 | describe "text-transform property" do 58 | 59 | "text-transform:inherit" `isRenderedFrom` (textTransform := inherit) 60 | 61 | "text-transform:initial" `isRenderedFrom` (textTransform := initial) 62 | 63 | "text-transform:unset" `isRenderedFrom` (textTransform := unset) 64 | 65 | "text-transform:none" `isRenderedFrom` (textTransform := none) 66 | 67 | "text-transform:capitalize" `isRenderedFrom` (textTransform := capitalize) 68 | 69 | "text-transform:uppercase" `isRenderedFrom` (textTransform := uppercase) 70 | 71 | "text-transform:lowercase" `isRenderedFrom` (textTransform := lowercase) 72 | 73 | "text-transform:full-width full-size-kana" 74 | `isRenderedFrom` 75 | (textTransform := fullWidth ~ fullSizeKana) 76 | 77 | "text-transform:capitalize full-width" 78 | `isRenderedFrom` 79 | (textTransform := capitalize ~ fullWidth) 80 | 81 | "text-transform:capitalize full-size-kana" 82 | `isRenderedFrom` 83 | (textTransform := capitalize ~ fullSizeKana) 84 | 85 | "text-transform:capitalize full-width full-size-kana" 86 | `isRenderedFrom` 87 | (textTransform := capitalize ~ fullWidth ~ fullSizeKana) 88 | 89 | describe "white-space property" do 90 | 91 | "white-space:inherit" `isRenderedFrom` (whiteSpace := inherit) 92 | 93 | "white-space:initial" `isRenderedFrom` (whiteSpace := initial) 94 | 95 | "white-space:unset" `isRenderedFrom` (whiteSpace := unset) 96 | 97 | "white-space:normal" `isRenderedFrom` (whiteSpace := normal) 98 | 99 | "white-space:pre" `isRenderedFrom` (whiteSpace := pre) 100 | 101 | "white-space:nowrap" `isRenderedFrom` (whiteSpace := nowrap) 102 | 103 | "white-space:pre-wrap" `isRenderedFrom` (whiteSpace := preWrap) 104 | 105 | "white-space:break-spaces" `isRenderedFrom` (whiteSpace := breakSpaces) 106 | 107 | "white-space:pre-line" `isRenderedFrom` (whiteSpace := preLine) 108 | 109 | describe "text-align property" do 110 | 111 | "text-align:inherit" `isRenderedFrom` (textAlign := inherit) 112 | 113 | "text-align:initial" `isRenderedFrom` (textAlign := initial) 114 | 115 | "text-align:unset" `isRenderedFrom` (textAlign := unset) 116 | 117 | "text-align:start" `isRenderedFrom` (textAlign := start) 118 | 119 | "text-align:end" `isRenderedFrom` (textAlign := end) 120 | 121 | "text-align:left" `isRenderedFrom` (textAlign := left) 122 | 123 | "text-align:right" `isRenderedFrom` (textAlign := right) 124 | 125 | "text-align:center" `isRenderedFrom` (textAlign := center) 126 | 127 | "text-align:justify" `isRenderedFrom` (textAlign := justify) 128 | 129 | "text-align:match-parent" `isRenderedFrom` (textAlign := matchParent) 130 | 131 | "text-align:justify-all" `isRenderedFrom` (textAlign := justifyAll) 132 | 133 | describe "word-spacing" do 134 | 135 | "word-spacing:inherit" `isRenderedFrom` (wordSpacing := inherit) 136 | 137 | "word-spacing:initial" `isRenderedFrom` (wordSpacing := initial) 138 | 139 | "word-spacing:unset" `isRenderedFrom` (wordSpacing := unset) 140 | 141 | "word-spacing:normal" `isRenderedFrom` (wordSpacing := normal) 142 | 143 | "word-spacing:0.025em" `isRenderedFrom` (wordSpacing := em 0.025) 144 | 145 | describe "letter-spacing" do 146 | 147 | "letter-spacing:inherit" `isRenderedFrom` (letterSpacing := inherit) 148 | 149 | "letter-spacing:initial" `isRenderedFrom` (letterSpacing := initial) 150 | 151 | "letter-spacing:unset" `isRenderedFrom` (letterSpacing := unset) 152 | 153 | "letter-spacing:normal" `isRenderedFrom` (letterSpacing := normal) 154 | 155 | "letter-spacing:0.025em" `isRenderedFrom` (letterSpacing := em 0.025) 156 | 157 | describe "text-indent property" do 158 | 159 | "text-indent:inherit" `isRenderedFrom` (textIndent := inherit) 160 | 161 | "text-indent:initial" `isRenderedFrom` (textIndent := initial) 162 | 163 | "text-indent:unset" `isRenderedFrom` (textIndent := unset) 164 | 165 | "text-indent:10px" `isRenderedFrom` (textIndent := px 10) 166 | 167 | "text-indent:2.5%" `isRenderedFrom` (textIndent := pct 2.5) 168 | 169 | describe "word-break property" do 170 | 171 | "word-break:inherit" `isRenderedFrom` (wordBreak := inherit) 172 | 173 | "word-break:initial" `isRenderedFrom` (wordBreak := initial) 174 | 175 | "word-break:unset" `isRenderedFrom` (wordBreak := unset) 176 | 177 | "word-break:normal" `isRenderedFrom` (wordBreak := normal) 178 | 179 | "word-break:keep-all" `isRenderedFrom` (wordBreak := keepAll) 180 | 181 | "word-break:break-all" `isRenderedFrom` (wordBreak := breakAll) 182 | 183 | "word-break:break-word" `isRenderedFrom` (wordBreak := breakWord) -------------------------------------------------------------------------------- /test/TransformsSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-transforms-1/ 2 | -- https://www.w3.org/TR/css-transforms-2/ 3 | 4 | module Test.TransformsSpec where 5 | 6 | import Prelude hiding (bottom, top) 7 | 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( bottom 11 | , center 12 | , deg 13 | , inherit 14 | , initial 15 | , left 16 | , matrix 17 | , matrix3d 18 | , none 19 | , pct 20 | , perspective 21 | , px 22 | , rad 23 | , right 24 | , rotate 25 | , rotate3d 26 | , rotateX 27 | , rotateY 28 | , rotateZ 29 | , scale 30 | , scale3d 31 | , scaleX 32 | , scaleY 33 | , scaleZ 34 | , skewX 35 | , skewY 36 | , top 37 | , transform 38 | , transformOrigin 39 | , translate 40 | , translate3d 41 | , translateX 42 | , translateY 43 | , translateZ 44 | , turn 45 | , unset 46 | , (:=) 47 | , (~) 48 | ) 49 | import Test.Spec (Spec, describe) 50 | import Test.Util (isRenderedFromInline, isRenderedFromVal) 51 | 52 | spec :: Spec Unit 53 | spec = 54 | describe "Transforms Module" do 55 | 56 | describe "2D Transform Functions" do 57 | 58 | let isRenderedFrom = isRenderedFromVal 59 | 60 | "matrix(1.2,0.2,-1,0.9,0,20)" 61 | `isRenderedFrom` 62 | matrix 1.2 0.2 (-1) 0.9 0 20 63 | 64 | "matrix(0.4,0,0.5,1.2,60,10)" 65 | `isRenderedFrom` 66 | matrix 0.4 0 0.5 1.2 60 10 67 | 68 | "matrix(0,1,1,0,0,0)" `isRenderedFrom` matrix 0 1 1 0 0 0 69 | 70 | "matrix(0.1,1,-0.3,1,0,0)" `isRenderedFrom` matrix 0.1 1 (-0.3) 1 0 0 71 | 72 | "translate(10px,10%)" `isRenderedFrom` translate (px 10) (pct 10) 73 | 74 | "translate(10%,10px)" `isRenderedFrom` translate (pct 10) (px 10) 75 | 76 | "translateX(10%)" `isRenderedFrom` translateX (pct 10) 77 | 78 | "translateX(10px)" `isRenderedFrom` translateX (px 10) 79 | 80 | "translateY(10%)" `isRenderedFrom` translateY (pct 10) 81 | 82 | "translateY(10px)" `isRenderedFrom` translateY (px 10) 83 | 84 | "translateY(10px)" `isRenderedFrom` translateY (px 10) 85 | 86 | "scale(0.5,0.75)" `isRenderedFrom` scale 0.5 0.75 87 | 88 | "scaleX(0.5)" `isRenderedFrom` scaleX 0.5 89 | 90 | "scaleY(0.5)" `isRenderedFrom` scaleY 0.5 91 | 92 | "rotate(0.25turn)" `isRenderedFrom` rotate (turn 0.25) 93 | 94 | "skewX(10deg)" `isRenderedFrom` skewX (deg 10) 95 | 96 | "skewY(75deg)" `isRenderedFrom` skewY (deg 75) 97 | 98 | describe "3D Transform Functions" do 99 | 100 | let isRenderedFrom = isRenderedFromVal 101 | 102 | "matrix3d(-0.6,1.34788,0,0,-2.34788,-0.6,0,0,0,0,1,0,0,0,10,1)" 103 | `isRenderedFrom` 104 | matrix3d (-0.6) 1.34788 0 0 (-2.34788) (-0.6) 0 0 0 0 1 0 0 0 10 1 105 | 106 | "matrix3d(0.5,0,-0.866025,0,0.595877,1.2,-1.03209,0,0.866025,0,0.5,0,25.9808,0,15,1)" 107 | `isRenderedFrom` 108 | matrix3d 0.5 0 (-0.866025) 0 0.595877 1.2 (-1.03209) 0 0.866025 0 0.5 109 | 0 110 | 25.9808 111 | 0 112 | 15 113 | 1 114 | 115 | "translate3d(10%,50px,10px)" 116 | `isRenderedFrom` 117 | translate3d (pct 10) (px 50) (px 10) 118 | 119 | "translate3d(50px,10%,10px)" 120 | `isRenderedFrom` 121 | translate3d (px 50) (pct 10) (px 10) 122 | 123 | "translateZ(10px)" `isRenderedFrom` translateZ (px 10) 124 | 125 | "scale3d(1,1,1)" `isRenderedFrom` scale3d 1 1 1 126 | 127 | "scale3d(-1.4,0.4,0.7)" `isRenderedFrom` scale3d (-1.4) 0.4 0.7 128 | 129 | "scaleZ(1)" `isRenderedFrom` scaleZ 1 130 | 131 | "scaleZ(-1.4)" `isRenderedFrom` scaleZ (-1.4) 132 | 133 | "rotate3d(1,1,1,45deg)" `isRenderedFrom` rotate3d 1 1 1 (deg 45) 134 | 135 | "rotate3d(2,-1,-1,-0.2turn)" 136 | `isRenderedFrom` 137 | rotate3d 2 (-1) (-1) (turn (-0.2)) 138 | 139 | "rotate3d(0.5,1.5,0.5,3.142rad)" 140 | `isRenderedFrom` 141 | rotate3d 0.5 1.5 0.5 (rad 3.142) 142 | 143 | "rotateX(45deg)" `isRenderedFrom` rotateX (deg 45) 144 | 145 | "rotateY(45deg)" `isRenderedFrom` rotateY (deg 45) 146 | 147 | "rotateZ(45deg)" `isRenderedFrom` rotateZ (deg 45) 148 | 149 | "perspective(50px)" `isRenderedFrom` perspective (px 50) 150 | 151 | "perspective(none)" `isRenderedFrom` perspective none 152 | 153 | describe "transform property" do 154 | 155 | let isRenderedFrom = isRenderedFromInline 156 | 157 | "transform:inherit" `isRenderedFrom` (transform := inherit) 158 | 159 | "transform:initial" `isRenderedFrom` (transform := initial) 160 | 161 | "transform:unset" `isRenderedFrom` (transform := unset) 162 | 163 | "transform:none" `isRenderedFrom` (transform := none) 164 | 165 | "transform:scaleX(1.5)" `isRenderedFrom` (transform := scaleX 1.5) 166 | 167 | "transform:rotateX(45deg) translateX(-10px) scaleX(1.5)" 168 | `isRenderedFrom` 169 | (transform := rotateX (deg 45) /\ translateX (px (-10)) /\ scaleX 1.5) 170 | 171 | describe "transform-origin property" do 172 | 173 | let isRenderedFrom = isRenderedFromInline 174 | 175 | "transform-origin:inherit" `isRenderedFrom` (transformOrigin := inherit) 176 | 177 | "transform-origin:initial" `isRenderedFrom` (transformOrigin := initial) 178 | 179 | "transform-origin:unset" `isRenderedFrom` (transformOrigin := unset) 180 | 181 | "transform-origin:left" `isRenderedFrom` (transformOrigin := left) 182 | 183 | "transform-origin:center" `isRenderedFrom` (transformOrigin := center) 184 | 185 | "transform-origin:right" `isRenderedFrom` (transformOrigin := right) 186 | 187 | "transform-origin:top" `isRenderedFrom` (transformOrigin := top) 188 | 189 | "transform-origin:bottom" `isRenderedFrom` (transformOrigin := bottom) 190 | 191 | "transform-origin:10px" `isRenderedFrom` (transformOrigin := px 10) 192 | 193 | "transform-origin:10%" `isRenderedFrom` (transformOrigin := pct 10) 194 | 195 | "transform-origin:left top" 196 | `isRenderedFrom` 197 | (transformOrigin := left ~ top) 198 | 199 | "transform-origin:center top" 200 | `isRenderedFrom` 201 | (transformOrigin := center ~ top) 202 | 203 | "transform-origin:right top" 204 | `isRenderedFrom` 205 | (transformOrigin := right ~ top) 206 | 207 | "transform-origin:left center" 208 | `isRenderedFrom` 209 | (transformOrigin := left ~ center) 210 | 211 | "transform-origin:center center" 212 | `isRenderedFrom` 213 | (transformOrigin := center ~ center) 214 | 215 | "transform-origin:right center" 216 | `isRenderedFrom` 217 | (transformOrigin := right ~ center) 218 | 219 | "transform-origin:left bottom" 220 | `isRenderedFrom` 221 | (transformOrigin := left ~ bottom) 222 | 223 | "transform-origin:center bottom" 224 | `isRenderedFrom` 225 | (transformOrigin := center ~ bottom) 226 | 227 | "transform-origin:right bottom" 228 | `isRenderedFrom` 229 | (transformOrigin := right ~ bottom) 230 | 231 | "transform-origin:10px 20%" 232 | `isRenderedFrom` 233 | (transformOrigin := px 10 ~ pct 20) 234 | 235 | "transform-origin:20% 10px" 236 | `isRenderedFrom` 237 | (transformOrigin := pct 20 ~ px 10) 238 | 239 | "transform-origin:10px top" 240 | `isRenderedFrom` 241 | (transformOrigin := px 10 ~ top) 242 | 243 | "transform-origin:10% bottom" 244 | `isRenderedFrom` 245 | (transformOrigin := pct 10 ~ bottom) 246 | 247 | "transform-origin:left 10px" 248 | `isRenderedFrom` 249 | (transformOrigin := left ~ px 10) 250 | 251 | "transform-origin:right 20%" 252 | `isRenderedFrom` 253 | (transformOrigin := right ~ pct 20) 254 | 255 | "transform-origin:left 20% 10px" 256 | `isRenderedFrom` 257 | (transformOrigin := left ~ pct 20 ~ px 10) 258 | 259 | "transform-origin:10px top 20px" 260 | `isRenderedFrom` 261 | (transformOrigin := px 10 ~ top ~ px 20) 262 | 263 | "transform-origin:10px 20% 10px" 264 | `isRenderedFrom` 265 | (transformOrigin := px 10 ~ pct 20 ~ px 10) 266 | 267 | "transform-origin:20% 10px 20px" 268 | `isRenderedFrom` 269 | (transformOrigin := pct 20 ~ px 10 ~ px 20) 270 | -------------------------------------------------------------------------------- /test/TransitionsSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-transitions-1/ 2 | 3 | module Test.TransitionsSpec where 4 | 5 | import Prelude hiding (bottom, top) 6 | 7 | import Data.Tuple.Nested ((/\)) 8 | import Tecton 9 | ( all 10 | , bottom 11 | , cubicBezier 12 | , ease 13 | , easeIn 14 | , easeInOut 15 | , easeOut 16 | , end 17 | , flex 18 | , height 19 | , inherit 20 | , initial 21 | , inset 22 | , jumpBoth 23 | , jumpEnd 24 | , jumpNone 25 | , jumpStart 26 | , left 27 | , marginTop 28 | , ms 29 | , nil 30 | , none 31 | , right 32 | , sec 33 | , start 34 | , stepEnd 35 | , stepStart 36 | , steps 37 | , top 38 | , transitionDelay 39 | , transitionDuration 40 | , transitionProperty 41 | , transitionTimingFunction 42 | , unset 43 | , width 44 | , (:=) 45 | ) 46 | import Test.Spec (Spec, describe) 47 | import Test.Util (isRenderedFromInline) 48 | 49 | spec :: Spec Unit 50 | spec = do 51 | 52 | let isRenderedFrom = isRenderedFromInline 53 | 54 | describe "Transitions" do 55 | 56 | describe "transition-property property" do 57 | 58 | "transition-property:inherit" 59 | `isRenderedFrom` 60 | (transitionProperty := inherit) 61 | 62 | "transition-property:initial" 63 | `isRenderedFrom` 64 | (transitionProperty := initial) 65 | 66 | "transition-property:unset" `isRenderedFrom` (transitionProperty := unset) 67 | 68 | "transition-property:none" `isRenderedFrom` (transitionProperty := none) 69 | 70 | "transition-property:all" `isRenderedFrom` (transitionProperty := all) 71 | 72 | "transition-property:margin-top" 73 | `isRenderedFrom` 74 | (transitionProperty := marginTop) 75 | 76 | "transition-property:width,height" 77 | `isRenderedFrom` 78 | (transitionProperty := width /\ height) 79 | 80 | "transition-property:top,right,bottom,left" 81 | `isRenderedFrom` 82 | (transitionProperty := top /\ right /\ bottom /\ left) 83 | 84 | "transition-property:inset" `isRenderedFrom` (transitionProperty := inset) 85 | 86 | "transition-property:flex" `isRenderedFrom` (transitionProperty := flex) 87 | 88 | describe "transition-duration property" do 89 | 90 | "transition-duration:inherit" 91 | `isRenderedFrom` 92 | (transitionDuration := inherit) 93 | 94 | "transition-duration:initial" 95 | `isRenderedFrom` 96 | (transitionDuration := initial) 97 | 98 | "transition-duration:unset" 99 | `isRenderedFrom` 100 | (transitionDuration := unset) 101 | 102 | "transition-duration:90ms" `isRenderedFrom` (transitionDuration := ms 90) 103 | 104 | "transition-duration:2ms,0,1s" 105 | `isRenderedFrom` 106 | (transitionDuration := ms 2 /\ nil /\ sec 1) 107 | 108 | describe "transition-timing-function" do 109 | 110 | "transition-timing-function:inherit" 111 | `isRenderedFrom` 112 | (transitionTimingFunction := inherit) 113 | 114 | "transition-timing-function:initial" 115 | `isRenderedFrom` 116 | (transitionTimingFunction := initial) 117 | 118 | "transition-timing-function:unset" 119 | `isRenderedFrom` 120 | (transitionTimingFunction := unset) 121 | 122 | "transition-timing-function:ease" 123 | `isRenderedFrom` 124 | (transitionTimingFunction := ease) 125 | 126 | "transition-timing-function:ease-in" 127 | `isRenderedFrom` 128 | (transitionTimingFunction := easeIn) 129 | 130 | "transition-timing-function:ease-out" 131 | `isRenderedFrom` 132 | (transitionTimingFunction := easeOut) 133 | 134 | "transition-timing-function:ease-in-out" 135 | `isRenderedFrom` 136 | (transitionTimingFunction := easeInOut) 137 | 138 | "transition-timing-function:step-start" 139 | `isRenderedFrom` 140 | (transitionTimingFunction := stepStart) 141 | 142 | "transition-timing-function:step-end" 143 | `isRenderedFrom` 144 | (transitionTimingFunction := stepEnd) 145 | 146 | "transition-timing-function:cubic-bezier(0.1,0.7,1,0.1)" 147 | `isRenderedFrom` 148 | (transitionTimingFunction := cubicBezier 0.1 0.7 1 0.1) 149 | 150 | "transition-timing-function:steps(4,jump-start)" 151 | `isRenderedFrom` 152 | (transitionTimingFunction := steps 4 jumpStart) 153 | 154 | "transition-timing-function:steps(10,jump-end)" 155 | `isRenderedFrom` 156 | (transitionTimingFunction := steps 10 jumpEnd) 157 | 158 | "transition-timing-function:steps(20,jump-none)" 159 | `isRenderedFrom` 160 | (transitionTimingFunction := steps 20 jumpNone) 161 | 162 | "transition-timing-function:steps(5,jump-both)" 163 | `isRenderedFrom` 164 | (transitionTimingFunction := steps 5 jumpBoth) 165 | 166 | "transition-timing-function:steps(6,start)" 167 | `isRenderedFrom` 168 | (transitionTimingFunction := steps 6 start) 169 | 170 | "transition-timing-function:steps(8,end)" 171 | `isRenderedFrom` 172 | (transitionTimingFunction := steps 8 end) 173 | 174 | "transition-timing-function:ease,step-start,cubic-bezier(0.1,0.7,1,0.1)" 175 | `isRenderedFrom` 176 | ( transitionTimingFunction := 177 | ease /\ stepStart /\ cubicBezier 0.1 0.7 1 0.1 178 | ) 179 | 180 | describe "transition-delay property" do 181 | 182 | "transition-delay:inherit" 183 | `isRenderedFrom` 184 | (transitionDelay := inherit) 185 | 186 | "transition-delay:initial" 187 | `isRenderedFrom` 188 | (transitionDelay := initial) 189 | 190 | "transition-delay:unset" 191 | `isRenderedFrom` 192 | (transitionDelay := unset) 193 | 194 | "transition-delay:90ms" `isRenderedFrom` (transitionDelay := ms 90) 195 | 196 | "transition-delay:2ms,0,1s" 197 | `isRenderedFrom` 198 | (transitionDelay := ms 2 /\ nil /\ sec 1) 199 | -------------------------------------------------------------------------------- /test/UISpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-ui-4/ 2 | 3 | module Test.UISpec where 4 | 5 | import Prelude 6 | 7 | import Color (rgb) 8 | import Data.Tuple.Nested ((/\)) 9 | import Tecton 10 | ( alias 11 | , allScroll 12 | , appearance 13 | , auto 14 | , cell 15 | , colResize 16 | , contextMenu 17 | , copy 18 | , crosshair 19 | , cursor 20 | , dashed 21 | , default 22 | , dotted 23 | , double 24 | , eResize 25 | , ewResize 26 | , grab 27 | , grabbing 28 | , groove 29 | , help 30 | , inherit 31 | , initial 32 | , inset 33 | , invert 34 | , medium 35 | , menulistButton 36 | , move 37 | , nResize 38 | , neResize 39 | , neswResize 40 | , nil 41 | , noDrop 42 | , none 43 | , notAllowed 44 | , nsResize 45 | , nwResize 46 | , nwseResize 47 | , outlineColor 48 | , outlineOffset 49 | , outlineStyle 50 | , outlineWidth 51 | , outset 52 | , pointer 53 | , progress 54 | , px 55 | , ridge 56 | , rowResize 57 | , sResize 58 | , seResize 59 | , solid 60 | , swResize 61 | , text 62 | , textfield 63 | , thick 64 | , thin 65 | , transparent 66 | , unset 67 | , url 68 | , verticalText 69 | , wResize 70 | , wait 71 | , zoomIn 72 | , zoomOut 73 | , (:=) 74 | , (~) 75 | ) 76 | import Test.Spec (Spec, describe) 77 | import Test.Util (isRenderedFromInline) 78 | 79 | spec :: Spec Unit 80 | spec = do 81 | 82 | let isRenderedFrom = isRenderedFromInline 83 | 84 | describe "Basic User Interface Module" do 85 | 86 | describe "outline-width property" do 87 | 88 | "outline-width:inherit" `isRenderedFrom` (outlineWidth := inherit) 89 | 90 | "outline-width:initial" `isRenderedFrom` (outlineWidth := initial) 91 | 92 | "outline-width:unset" `isRenderedFrom` (outlineWidth := unset) 93 | 94 | "outline-width:1px" `isRenderedFrom` (outlineWidth := px 1) 95 | 96 | "outline-width:thin" `isRenderedFrom` (outlineWidth := thin) 97 | 98 | "outline-width:medium" `isRenderedFrom` (outlineWidth := medium) 99 | 100 | "outline-width:thick" `isRenderedFrom` (outlineWidth := thick) 101 | 102 | describe "outline-style property" do 103 | 104 | "outline-style:inherit" `isRenderedFrom` (outlineStyle := inherit) 105 | 106 | "outline-style:initial" `isRenderedFrom` (outlineStyle := initial) 107 | 108 | "outline-style:unset" `isRenderedFrom` (outlineStyle := unset) 109 | 110 | "outline-style:auto" `isRenderedFrom` (outlineStyle := auto) 111 | 112 | "outline-style:none" `isRenderedFrom` (outlineStyle := none) 113 | 114 | "outline-style:dotted" `isRenderedFrom` (outlineStyle := dotted) 115 | 116 | "outline-style:dashed" `isRenderedFrom` (outlineStyle := dashed) 117 | 118 | "outline-style:solid" `isRenderedFrom` (outlineStyle := solid) 119 | 120 | "outline-style:double" `isRenderedFrom` (outlineStyle := double) 121 | 122 | "outline-style:groove" `isRenderedFrom` (outlineStyle := groove) 123 | 124 | "outline-style:ridge" `isRenderedFrom` (outlineStyle := ridge) 125 | 126 | "outline-style:inset" `isRenderedFrom` (outlineStyle := inset) 127 | 128 | "outline-style:outset" `isRenderedFrom` (outlineStyle := outset) 129 | 130 | describe "outline-color property" do 131 | 132 | "outline-color:inherit" `isRenderedFrom` (outlineColor := inherit) 133 | 134 | "outline-color:initial" `isRenderedFrom` (outlineColor := initial) 135 | 136 | "outline-color:unset" `isRenderedFrom` (outlineColor := unset) 137 | 138 | "outline-color:#0000ff" `isRenderedFrom` (outlineColor := rgb 0 0 255) 139 | 140 | "outline-color:transparent" `isRenderedFrom` (outlineColor := transparent) 141 | 142 | "outline-color:invert" `isRenderedFrom` (outlineColor := invert) 143 | 144 | describe "outline-offset property" do 145 | 146 | "outline-offset:inherit" `isRenderedFrom` (outlineOffset := inherit) 147 | 148 | "outline-offset:initial" `isRenderedFrom` (outlineOffset := initial) 149 | 150 | "outline-offset:unset" `isRenderedFrom` (outlineOffset := unset) 151 | 152 | "outline-offset:4px" `isRenderedFrom` (outlineOffset := px 4) 153 | 154 | "outline-offset:0" `isRenderedFrom` (outlineOffset := nil) 155 | 156 | describe "cursor" do 157 | 158 | "cursor:inherit" `isRenderedFrom` (cursor := inherit) 159 | 160 | "cursor:initial" `isRenderedFrom` (cursor := initial) 161 | 162 | "cursor:unset" `isRenderedFrom` (cursor := unset) 163 | 164 | "cursor:auto" `isRenderedFrom` (cursor := auto) 165 | 166 | "cursor:default" `isRenderedFrom` (cursor := default) 167 | 168 | "cursor:none" `isRenderedFrom` (cursor := none) 169 | 170 | "cursor:context-menu" `isRenderedFrom` (cursor := contextMenu) 171 | 172 | "cursor:help" `isRenderedFrom` (cursor := help) 173 | 174 | "cursor:pointer" `isRenderedFrom` (cursor := pointer) 175 | 176 | "cursor:progress" `isRenderedFrom` (cursor := progress) 177 | 178 | "cursor:wait" `isRenderedFrom` (cursor := wait) 179 | 180 | "cursor:cell" `isRenderedFrom` (cursor := cell) 181 | 182 | "cursor:crosshair" `isRenderedFrom` (cursor := crosshair) 183 | 184 | "cursor:text" `isRenderedFrom` (cursor := text) 185 | 186 | "cursor:vertical-text" `isRenderedFrom` (cursor := verticalText) 187 | 188 | "cursor:alias" `isRenderedFrom` (cursor := alias) 189 | 190 | "cursor:copy" `isRenderedFrom` (cursor := copy) 191 | 192 | "cursor:move" `isRenderedFrom` (cursor := move) 193 | 194 | "cursor:no-drop" `isRenderedFrom` (cursor := noDrop) 195 | 196 | "cursor:not-allowed" `isRenderedFrom` (cursor := notAllowed) 197 | 198 | "cursor:grab" `isRenderedFrom` (cursor := grab) 199 | 200 | "cursor:grabbing" `isRenderedFrom` (cursor := grabbing) 201 | 202 | "cursor:e-resize" `isRenderedFrom` (cursor := eResize) 203 | 204 | "cursor:n-resize" `isRenderedFrom` (cursor := nResize) 205 | 206 | "cursor:ne-resize" `isRenderedFrom` (cursor := neResize) 207 | 208 | "cursor:nw-resize" `isRenderedFrom` (cursor := nwResize) 209 | 210 | "cursor:s-resize" `isRenderedFrom` (cursor := sResize) 211 | 212 | "cursor:se-resize" `isRenderedFrom` (cursor := seResize) 213 | 214 | "cursor:sw-resize" `isRenderedFrom` (cursor := swResize) 215 | 216 | "cursor:w-resize" `isRenderedFrom` (cursor := wResize) 217 | 218 | "cursor:ew-resize" `isRenderedFrom` (cursor := ewResize) 219 | 220 | "cursor:ns-resize" `isRenderedFrom` (cursor := nsResize) 221 | 222 | "cursor:nesw-resize" `isRenderedFrom` (cursor := neswResize) 223 | 224 | "cursor:nwse-resize" `isRenderedFrom` (cursor := nwseResize) 225 | 226 | "cursor:col-resize" `isRenderedFrom` (cursor := colResize) 227 | 228 | "cursor:row-resize" `isRenderedFrom` (cursor := rowResize) 229 | 230 | "cursor:all-scroll" `isRenderedFrom` (cursor := allScroll) 231 | 232 | "cursor:zoom-in" `isRenderedFrom` (cursor := zoomIn) 233 | 234 | "cursor:zoom-out" `isRenderedFrom` (cursor := zoomOut) 235 | 236 | "cursor:url(\"example.svg#linkcursor\"),url(\"hyper.cur\"),url(\"hyper.png\") 2 3,pointer" 237 | `isRenderedFrom` 238 | ( cursor := url "example.svg#linkcursor" /\ url "hyper.cur" 239 | /\ url "hyper.png" 240 | ~ 2 241 | ~ 3 242 | /\ pointer 243 | ) 244 | 245 | describe "appearance property" do 246 | 247 | "appearance:inherit" `isRenderedFrom` (appearance := inherit) 248 | 249 | "appearance:initial" `isRenderedFrom` (appearance := initial) 250 | 251 | "appearance:unset" `isRenderedFrom` (appearance := unset) 252 | 253 | "appearance:none" `isRenderedFrom` (appearance := none) 254 | 255 | "appearance:auto" `isRenderedFrom` (appearance := auto) 256 | 257 | "appearance:textfield" `isRenderedFrom` (appearance := textfield) 258 | 259 | "appearance:menulist-button" 260 | `isRenderedFrom` 261 | (appearance := menulistButton) -------------------------------------------------------------------------------- /test/UnsafeDeclarationSpec.purs: -------------------------------------------------------------------------------- 1 | module Test.UnsafeDeclarationSpec where 2 | 3 | import Prelude 4 | 5 | import Tecton 6 | ( KeyframesName(..) 7 | , keyframes 8 | , pct 9 | , universal 10 | , unsafeDeclaration 11 | , (?) 12 | ) 13 | import Test.Spec (Spec, describe) 14 | import Test.Util (isRenderedFromSheet) 15 | 16 | spec :: Spec Unit 17 | spec = do 18 | 19 | let isRenderedFrom = isRenderedFromSheet 20 | 21 | describe "unsafeDeclaration" do 22 | 23 | "*{-webkit-text-stroke-width:thin}" 24 | `isRenderedFrom` do 25 | universal ? 26 | unsafeDeclaration "-webkit-text-stroke-width" "thin" 27 | 28 | "@keyframes foo{0%{-moz-opacity:0}100%{-moz-opacity:1}}" 29 | `isRenderedFrom` do 30 | keyframes (KeyframesName "foo") ? do 31 | pct 0 ? 32 | unsafeDeclaration "-moz-opacity" "0" 33 | pct 100 ? 34 | unsafeDeclaration "-moz-opacity" "1" -------------------------------------------------------------------------------- /test/Util.purs: -------------------------------------------------------------------------------- 1 | module Test.Util where 2 | 3 | import Prelude 4 | 5 | import Control.Monad.Writer (Writer) 6 | import Data.List (List) 7 | import Tecton.Internal 8 | ( class ToVal 9 | , Declaration' 10 | , Statement 11 | , compact 12 | , renderInline' 13 | , renderSheet 14 | , runVal 15 | , val 16 | ) 17 | import Test.Spec (Spec, it) 18 | import Test.Spec.Assertions (shouldEqual) 19 | 20 | isRenderedFromInline 21 | :: forall ps 22 | . String 23 | -> Writer (List Declaration') ps 24 | -> Spec Unit 25 | isRenderedFromInline expected given = 26 | it ("renders " <> expected) $ 27 | renderInline' compact given `shouldEqual` expected 28 | 29 | isRenderedFromSheet 30 | :: String 31 | -> Writer (List Statement) Unit 32 | -> Spec Unit 33 | isRenderedFromSheet expected given = 34 | it ("renders " <> expected) $ 35 | renderSheet compact given `shouldEqual` expected 36 | 37 | isRenderedFromVal 38 | :: forall a 39 | . ToVal a 40 | => String 41 | -> a 42 | -> Spec Unit 43 | isRenderedFromVal expected given = 44 | it ("renders " <> expected) $ 45 | runVal compact (val given) `shouldEqual` expected 46 | -------------------------------------------------------------------------------- /test/VisufxSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/CSS2/visufx.html 2 | 3 | module Test.VisufxSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( collapse 9 | , hidden 10 | , inherit 11 | , initial 12 | , unset 13 | , visibility 14 | , visible 15 | , (:=) 16 | ) 17 | import Test.Spec (Spec, describe) 18 | import Test.Util (isRenderedFromInline) 19 | 20 | spec :: Spec Unit 21 | spec = do 22 | 23 | let isRenderedFrom = isRenderedFromInline 24 | 25 | describe "Visual Effects" do 26 | 27 | describe "visibility property" do 28 | 29 | "visibility:inherit" `isRenderedFrom` (visibility := inherit) 30 | 31 | "visibility:initial" `isRenderedFrom` (visibility := initial) 32 | 33 | "visibility:unset" `isRenderedFrom` (visibility := unset) 34 | 35 | "visibility:visible" `isRenderedFrom` (visibility := visible) 36 | 37 | "visibility:hidden" `isRenderedFrom` (visibility := hidden) 38 | 39 | "visibility:collapse" `isRenderedFrom` (visibility := collapse) 40 | -------------------------------------------------------------------------------- /test/VisurenSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/CSS2/visuren.html 2 | 3 | module Test.VisurenSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton 8 | ( auto 9 | , both 10 | , clear 11 | , float 12 | , inherit 13 | , initial 14 | , left 15 | , none 16 | , right 17 | , unset 18 | , zIndex 19 | , (:=) 20 | ) 21 | import Test.Spec (Spec, describe) 22 | import Test.Util (isRenderedFromInline) 23 | 24 | spec :: Spec Unit 25 | spec = do 26 | 27 | let isRenderedFrom = isRenderedFromInline 28 | 29 | describe "Visual Formatting Model" do 30 | 31 | describe "float property" do 32 | 33 | "float:inherit" `isRenderedFrom` (float := inherit) 34 | 35 | "float:initial" `isRenderedFrom` (float := initial) 36 | 37 | "float:unset" `isRenderedFrom` (float := unset) 38 | 39 | "float:left" `isRenderedFrom` (float := left) 40 | 41 | "float:right" `isRenderedFrom` (float := right) 42 | 43 | "float:none" `isRenderedFrom` (float := none) 44 | 45 | describe "clear property" do 46 | 47 | "clear:inherit" `isRenderedFrom` (clear := inherit) 48 | 49 | "clear:initial" `isRenderedFrom` (clear := initial) 50 | 51 | "clear:unset" `isRenderedFrom` (clear := unset) 52 | 53 | "clear:none" `isRenderedFrom` (clear := none) 54 | 55 | "clear:left" `isRenderedFrom` (clear := left) 56 | 57 | "clear:right" `isRenderedFrom` (clear := right) 58 | 59 | "clear:both" `isRenderedFrom` (clear := both) 60 | 61 | describe "z-index property" do 62 | 63 | "z-index:inherit" `isRenderedFrom` (zIndex := inherit) 64 | 65 | "z-index:initial" `isRenderedFrom` (zIndex := initial) 66 | 67 | "z-index:unset" `isRenderedFrom` (zIndex := unset) 68 | 69 | "z-index:auto" `isRenderedFrom` (zIndex := auto) 70 | 71 | "z-index:3" `isRenderedFrom` (zIndex := 3) 72 | -------------------------------------------------------------------------------- /test/WritingModesSpec.purs: -------------------------------------------------------------------------------- 1 | -- https://www.w3.org/TR/css-writing-modes-4/ 2 | 3 | module Test.WritingModesSpec where 4 | 5 | import Prelude 6 | 7 | import Tecton (direction, inherit, initial, ltr, rtl, unset, (:=)) 8 | import Test.Spec (Spec, describe) 9 | import Test.Util (isRenderedFromInline) 10 | 11 | spec :: Spec Unit 12 | spec = do 13 | 14 | let isRenderedFrom = isRenderedFromInline 15 | 16 | describe "Writing Modes Module" do 17 | 18 | describe "direction property" do 19 | 20 | "direction:inherit" `isRenderedFrom` (direction := inherit) 21 | 22 | "direction:initial" `isRenderedFrom` (direction := initial) 23 | 24 | "direction:unset" `isRenderedFrom` (direction := unset) 25 | 26 | "direction:ltr" `isRenderedFrom` (direction := ltr) 27 | 28 | "direction:rtl" `isRenderedFrom` (direction := rtl) 29 | --------------------------------------------------------------------------------