All Projects → Risto-Stevcev → bs-declaredom

Risto-Stevcev / bs-declaredom

Licence: BSD-3-Clause license
Strongly typed declarative markup for the DOM and CSS

Programming Languages

ocaml
1615 projects

Projects that are alternatives of or similar to bs-declaredom

attoparser
A tiny but fast java event-style markup parser.
Stars: ✭ 46 (-30.3%)
Mutual labels:  dom, markup
MapML
Map Markup Language is hypertext for Web maps, like HTML is hypertext for Web pages https://maps4html.org/MapML/spec/
Stars: ✭ 48 (-27.27%)
Mutual labels:  dom, declarative
Preact Markup
⚡️ Render HTML5 as VDOM, with Components as Custom Elements!
Stars: ✭ 167 (+153.03%)
Mutual labels:  dom, markup
react-is-scrolling
Simply detect if users are scrolling in your components in a declarative API
Stars: ✭ 17 (-74.24%)
Mutual labels:  declarative
granary
Tezos smart contract & dapp development toolkit
Stars: ✭ 67 (+1.52%)
Mutual labels:  reasonml
hast-util-from-dom
utility to transform a DOM tree to hast
Stars: ✭ 20 (-69.7%)
Mutual labels:  dom
reason-epitath
CPS sugar usage for React Render Props composition in ReasonML
Stars: ✭ 16 (-75.76%)
Mutual labels:  reasonml
flyteidl
Specification of the IR for Flyte workflows and tasks. Also Interfaces for all backend services. https://docs.flyte.org/projects/flyteidl/en/stable/
Stars: ✭ 27 (-59.09%)
Mutual labels:  declarative
slackdown
A simple Slack message text formatting to HTML code converter.
Stars: ✭ 27 (-59.09%)
Mutual labels:  markup
zestdb
ZestDB
Stars: ✭ 18 (-72.73%)
Mutual labels:  reasonml
remotedata-re
Tools for fetching data from remote sources in Reason
Stars: ✭ 32 (-51.52%)
Mutual labels:  reasonml
dom-lite
A small DOM library for server-side testing, rendering, and handling of HTML files
Stars: ✭ 18 (-72.73%)
Mutual labels:  dom
Web-Map-Custom-Element
A custom <mapml-viewer> and <layer-> element suite
Stars: ✭ 49 (-25.76%)
Mutual labels:  dom
hypercomponent
⚡ Fast and light component system, backed by hyperHTML
Stars: ✭ 45 (-31.82%)
Mutual labels:  dom
html5parser
A super tiny and fast html5 AST parser.
Stars: ✭ 153 (+131.82%)
Mutual labels:  dom
sparrowql
Declarative MongoDB aggregations.
Stars: ✭ 28 (-57.58%)
Mutual labels:  declarative
InDiv
an angular like web mvvm framework.一个类 angular 前端框架。https://dimalilongji.github.io/InDiv
Stars: ✭ 88 (+33.33%)
Mutual labels:  dom
dclareForMPS
Adding declarative, reactive and incremental rules to MPS
Stars: ✭ 21 (-68.18%)
Mutual labels:  declarative
bs-spectacle
No description or website provided.
Stars: ✭ 15 (-77.27%)
Mutual labels:  reasonml
rr-2048
2048 game in Reason React
Stars: ✭ 15 (-77.27%)
Mutual labels:  reasonml

bs-declaredom

Build Status Latest release License

Strongly typed declarative markup for the DOM and CSS

  • Complete - HTML and CSS specifications are fully implemented!
  • Strongly typed - No more markup or styling silently failing without any guidance
  • Custom Elements - Create custom components that can leverage strong typing
  • CSS Modules - Builtin support for modular CSS with strong typing
  • Tree Shaking - Remove unused HTML and CSS from the final bundle with tree shaking
  • MVC - Combine with other tooling for get all of the functionality of frameworks without the headaches

Install

npm install --save @ristostevcev/bs-declaredom

Make sure to set package-specs.module in your project's bsconfig.json to es6-global to compile to ES modules for the browser.

If you're using declaredom to generate static HTML or CSS on the backend, add an additional package-spec for commonjs in your bsconfig.json. If you're generating static HTML on the backend, make sure to also install and initialize jsdom and set global.window and global.document, like so.

Examples

The examples in this README can be found in the example/ folder.

A Library Or A Framework

Declaredom is intentionally simple. It's intended to do only one thing and one thing well, which is to provide good declarative markup for HTML and CSS.

That being said, it's also intended to be combined with other tools so that you get all of the functionality that you would get from a framework.

There are several advantages to combining several small libraries that only do one thing instead of a large monolithic framework:

  • Only use what you need - Keep things simple, only pull in extra functionality if it provides value.
  • Highly customizable - By separating concerns you can tailor everything to your needs, you aren't forced to follow any one specific pattern.
  • Encourages open source contribution - Callbag follows a similar philosophy and has arguably produced higher quality contributions compared to other js streaming libraries, because it's decentralized and there's no authority you have to get approval from. You can write your own callbag libraries and nobody can stop you.
  • Easier to reason about - By having several very small libraries instead of one very large one, it becomes much more tractable for developers to peek at the source code to understand what's going on under the hood.

See the example MVC todo app for more details.

Introduction

This library provides sound static typing guarantees for HTML and CSS. It ensures that you write correct HTML and CSS in your app with good conventions like CSS modules. This library is based off of and fully compliant with the HTML and CSS specs (see docs).

The HTML that's generated are actual DOM nodes that can be converted into bs-webapi-incubator's Dom.element types using Html.Node.to_element, or to a Dom.node using Html.Node.to_node if it's a text node or document fragment.

Documentation

See the Html and Css module docs

JSX (Reason) or Ocaml

Use either JSX or vanilla Ocaml depending on which style you prefer. Example JSX:

let foo: Html.Node.t([> Html.Node.div]) =
  <div>
    <span style=Css.inline(~color=`green, ())>
      <text>"Hello, world!"</text>
    </span>
    <br/>
  </div>

The <text> markup refers to a text node.

Strongly typed CSS

Don't worry anymore about CSS silently failing! this library ensures that you only apply CSS styles and attributes that are valid for the element

let _ =
  div ~id:"some_div"
    (* This fails because div elements are block elements - the vertical-align
     * property applies to inline elements
    ~style:(Css.inline ~vertical_align:`baseline ())
     *)
    ~style:(Css.block ~color:`red ())
    [||]

You can override the CSS display styles, but this library intentionally restricts this functionality to the generic flow (<div>), phrasing (<span>) and sectioning (<section>) containers as other use cases are usually a CSS antipattern

let _ =
  Div.flex ~style:(Css.flexbox ~justify_content:`center ()) [|
    Span.inline_block ~style:(Css.inline_block ~color:`blue ()) [|text "foo"|];
    Section.inline_flex [|text "bar"|]
  |]

Strongly typed HTML

Only valid children are allowed for each element

let _ =
  html [|
    (* This fails because the <html> tag only takes a <head> or <body> element 
     * as children
    span [|text "foo"|]
     *)
    head [||]
  |]

Only valid attributes are allowed for each element. The anchor tag accepts a href attribute, and the link aria role is also allowed

let anchor =
  a ~id:"link" ~href:"#"
    (* Anchor elements can accept the `link` aria role *)
    ~aria:(Html.Attributes.Aria.link ~aria_hidden:() ~aria_label:"foo" ())
    ~on_click:(fun _ -> Js.log "clicked!")
    [|text "some link"|]

Target specific HTML in functions

Make functions that only take a specific kind of element or group of elements

let f (_: Html.Node.span Html.Node.t): unit = ()
let _ =
  f (span [|text "hello"|])

Custom HTML

Add custom types. This creates a custom type called foo. This also works very well with web components

let custom_foo: [> [> `foo] Html.Node.custom] Html.Node.t = Obj.magic @@
  span [|text "foo"|]

You can even give your custom element the ability to take only specific children! OCaml's powerful polymorphic variants will correctly unify the elements you pass into the array.

This example constructs a custom foo element that only takes other custom foo elements, custom bar elements, and span and br elements:

let make_custom_bar
  (children: [[`bar | `foo] Html.Node.custom | Html.Node.span | Html.Node.br] Html.Node.t array):
  [> [> `bar] Html.Node.custom] Html.Node.t =
  (* TODO: stubbed, needs implementation *)
  Obj.magic children

let _: [`bar] Html.Node.custom Html.Node.t =
  make_custom_bar
  [|
    span [|text "Custom foo:"|];
    br ();
    custom_foo;
    (* This fails because <p> tags aren't allowed
    p [|text "foobar"|]
     *)
  |]

You can also typecheck based on your custom type

let f' (_: [`foo] Html.Node.custom Html.Node.t): unit = ()
let _ = f' custom_foo

And since the markup produces real HTML elements, it works very well with web components.

CSS Modules

CSS modules deal with a lot of the pitfalls of CSS in a large scale app. Provide one CSS module per component and no longer worry about precedence rules, or enforcing conventions like BEM, or applying silly hacks and refactoring if the dev team painted themselves into a corner. Keep it simple.

let my_title = Css.Module.make @@
  Css.block ~color:`coral ~font_size:(`em 24.) ()

Instead of mucking around with inheritance using CSS's inheritance model, you can build up abstractions using composition instead by merging rulesets, which is more explicit and easier to understand and predict.

You can then apply these to your elements, but make sure you serve the CSS module in a stylesheet (inline or served as a CSS file)

let _ =
  h1 ~css_module:my_title [|text "This is my title"|]

These work much like postcss modules except that you get to use Ocaml's type system to ensure that you only reference a module that actually exists, and no preprocessing is required.

And you can still build these if you aren't using Ocaml on the backend by generating the CSS stylesheets as part of your build, and then you can even apply other transformations like autoprefixer if you need to.

Acknowledgments

This library is possible only because of Ocaml's powerful type system that is both strong, sound, and flexible. Speficially, it's Ocaml's module system and polymorphic variants that make this library possible.

Tree Shaking

This library generates es6-global and commonjs output so you can use the es6 output for tree-shaking using rollup or webpack and the commonjs output for a nodejs backend. The resulting bundle won't include any exports that you aren't using, such as HTML nodes or CSS.

Use rollup + google closure compiler on your final bundle if you want to get the most out of tree shaking and dead code elimination.

License

See LICENSE

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].