├── .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 | [](https://github.com/nsaunders/purescript-tecton/actions?query=workflow%3ACI+branch%3Amaster) [](https://github.com/nsaunders/purescript-tecton/releases) [](https://github.com/purescript/registry) [](https://pursuit.purescript.org/packages/purescript-tecton) [](./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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 | [](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 |
--------------------------------------------------------------------------------