or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-react.mdcss-styling.mddom-integration.mderror-boundaries.mdevent-system.mdindex.mdreact-hooks.mdrouting.mdserver-side-rendering.mdtesting-utilities.md
tile.json

css-styling.mddocs/

CSS Styling

Type-safe CSS styling system with comprehensive support for CSS properties including CSS Grid, Flexbox, animations, and SVG.

Core Types

type t; /* Style object type */

Style Creation

external make: (
  /* Layout & Positioning */
  ~display: string=?,
  ~position: string=?,
  ~top: string=?,
  ~right: string=?,
  ~bottom: string=?,
  ~left: string=?,
  ~width: string=?,
  ~height: string=?,
  ~minWidth: string=?,
  ~minHeight: string=?,
  ~maxWidth: string=?,
  ~maxHeight: string=?,
  ~zIndex: string=?,
  ~float: string=?,
  ~clear: string=?,
  ~overflow: string=?,
  ~overflowX: string=?,
  ~overflowY: string=?,
  ~visibility: string=?,
  ~opacity: string=?,
  
  /* Box Model */
  ~margin: string=?,
  ~marginTop: string=?,
  ~marginRight: string=?,
  ~marginBottom: string=?,
  ~marginLeft: string=?,
  ~padding: string=?,
  ~paddingTop: string=?,
  ~paddingRight: string=?,
  ~paddingBottom: string=?,
  ~paddingLeft: string=?,
  ~border: string=?,
  ~borderTop: string=?,
  ~borderRight: string=?,
  ~borderBottom: string=?,
  ~borderLeft: string=?,
  ~borderWidth: string=?,
  ~borderStyle: string=?,
  ~borderColor: string=?,
  ~borderRadius: string=?,
  ~boxShadow: string=?,
  ~boxSizing: string=?,
  
  /* Background */
  ~background: string=?,
  ~backgroundColor: string=?,
  ~backgroundImage: string=?,
  ~backgroundPosition: string=?,
  ~backgroundSize: string=?,
  ~backgroundRepeat: string=?,
  ~backgroundAttachment: string=?,
  ~backgroundClip: string=?,
  
  /* Typography */
  ~color: string=?,
  ~font: string=?,
  ~fontFamily: string=?,
  ~fontSize: string=?,
  ~fontWeight: string=?,
  ~fontStyle: string=?,
  ~fontVariant: string=?,
  ~lineHeight: string=?,
  ~letterSpacing: string=?,
  ~wordSpacing: string=?,
  ~textAlign: string=?,
  ~textDecoration: string=?,
  ~textTransform: string=?,
  ~textIndent: string=?,
  ~textShadow: string=?,
  ~whiteSpace: string=?,
  ~wordWrap: string=?,
  ~wordBreak: string=?,
  
  /* Flexbox */
  ~flex: string=?,
  ~flexDirection: string=?,
  ~flexWrap: string=?,
  ~flexFlow: string=?,
  ~justifyContent: string=?,
  ~alignItems: string=?,
  ~alignSelf: string=?,
  ~alignContent: string=?,
  ~flexGrow: string=?,
  ~flexShrink: string=?,
  ~flexBasis: string=?,
  ~order: string=?,
  
  /* Grid */
  ~grid: string=?,
  ~gridTemplate: string=?,
  ~gridTemplateRows: string=?,
  ~gridTemplateColumns: string=?,
  ~gridTemplateAreas: string=?,
  ~gridArea: string=?,
  ~gridRow: string=?,
  ~gridColumn: string=?,
  ~gridRowStart: string=?,
  ~gridRowEnd: string=?,
  ~gridColumnStart: string=?,
  ~gridColumnEnd: string=?,
  ~gridGap: string=?,
  ~gridRowGap: string=?,
  ~gridColumnGap: string=?,
  ~justifyItems: string=?,
  ~justifySelf: string=?,
  
  /* Transforms & Animation */
  ~transform: string=?,
  ~transformOrigin: string=?,
  ~transformStyle: string=?,
  ~perspective: string=?,
  ~perspectiveOrigin: string=?,
  ~backfaceVisibility: string=?,
  ~animation: string=?,
  ~animationName: string=?,
  ~animationDuration: string=?,
  ~animationTimingFunction: string=?,
  ~animationDelay: string=?,
  ~animationIterationCount: string=?,
  ~animationDirection: string=?,
  ~animationFillMode: string=?,
  ~animationPlayState: string=?,
  ~transition: string=?,
  ~transitionProperty: string=?,
  ~transitionDuration: string=?,
  ~transitionTimingFunction: string=?,
  ~transitionDelay: string=?,
  
  /* SVG */
  ~fill: string=?,
  ~fillOpacity: string=?,
  ~fillRule: string=?,
  ~stroke: string=?,
  ~strokeWidth: string=?,
  ~strokeOpacity: string=?,
  ~strokeDasharray: string=?,
  ~strokeDashoffset: string=?,
  ~strokeLinecap: string=?,
  ~strokeLinejoin: string=?,
  ~strokeMiterlimit: string=?,
  
  /* Lists */
  ~listStyle: string=?,
  ~listStyleType: string=?,
  ~listStylePosition: string=?,
  ~listStyleImage: string=?,
  
  /* Tables */
  ~tableLayout: string=?,
  ~borderCollapse: string=?,
  ~borderSpacing: string=?,
  ~captionSide: string=?,
  ~emptyCells: string=?,
  
  /* Content */
  ~content: string=?,
  ~quotes: string=?,
  ~counterReset: string=?,
  ~counterIncrement: string=?,
  
  /* User Interface */
  ~cursor: string=?,
  ~outline: string=?,
  ~outlineColor: string=?,
  ~outlineStyle: string=?,
  ~outlineWidth: string=?,
  ~resize: string=?,
  ~userSelect: string=?,
  ~pointerEvents: string=?,
  
  unit
) => t;

Style Utilities

/* Combine multiple styles */
external combine: (t, t) => t;

/* Unsafe escape hatches */
let unsafeAddProp: (t, string, string) => t;
external unsafeAddStyle: (t, Js.t({..})) => t;

Usage Examples

Basic Styling

let buttonStyle = ReactDOMStyle.make(
  ~backgroundColor="#007bff",
  ~color="white",
  ~border="none",
  ~borderRadius="4px",
  ~padding="8px 16px",
  ~fontSize="14px",
  ~cursor="pointer",
  ~transition="background-color 0.2s",
  ()
);

let hoverStyle = ReactDOMStyle.make(
  ~backgroundColor="#0056b3",
  ()
);

[@react.component]
let make = (~onClick, ~children) => {
  let (isHovered, setHovered) = React.useState(() => false);
  
  let finalStyle = isHovered 
    ? ReactDOMStyle.combine(buttonStyle, hoverStyle)
    : buttonStyle;
  
  <button
    style=finalStyle
    onClick
    onMouseEnter={_ => setHovered(_ => true)}
    onMouseLeave={_ => setHovered(_ => false)}
  >
    {children}
  </button>
}

Flexbox Layout

let containerStyle = ReactDOMStyle.make(
  ~display="flex",
  ~flexDirection="column",
  ~alignItems="center",
  ~justifyContent="space-between",
  ~minHeight="100vh",
  ~padding="20px",
  ()
);

let headerStyle = ReactDOMStyle.make(
  ~display="flex",
  ~justifyContent="space-between",
  ~alignItems="center",
  ~width="100%",
  ~marginBottom="20px",
  ()
);

[@react.component]
let make = () => {
  <div style=containerStyle>
    <header style=headerStyle>
      <h1> {React.string("My App")} </h1>
      <nav> {React.string("Navigation")} </nav>
    </header>
    <main style={ReactDOMStyle.make(~flex="1", ())}>
      {React.string("Content")}
    </main>
    <footer> {React.string("Footer")} </footer>
  </div>
}

CSS Grid

let gridStyle = ReactDOMStyle.make(
  ~display="grid",
  ~gridTemplateColumns="repeat(auto-fit, minmax(300px, 1fr))",
  ~gridGap="20px",
  ~padding="20px",
  ()
);

let cardStyle = ReactDOMStyle.make(
  ~border="1px solid #ddd",
  ~borderRadius="8px",
  ~padding="16px",
  ~backgroundColor="white",
  ~boxShadow="0 2px 4px rgba(0,0,0,0.1)",
  ()
);

[@react.component]
let make = (~items) => {
  <div style=gridStyle>
    {items->Array.map(item =>
      <div key=item.id style=cardStyle>
        <h3> {React.string(item.title)} </h3>
        <p> {React.string(item.description)} </p>
      </div>
    )->React.array}
  </div>
}

Animations

let spinKeyframes = ReactDOMStyle.make(
  ~animationName="spin",
  ~animationDuration="1s", 
  ~animationTimingFunction="linear",
  ~animationIterationCount="infinite",
  ()
);

let loadingStyle = ReactDOMStyle.combine(
  ReactDOMStyle.make(
    ~width="40px",
    ~height="40px",
    ~border="4px solid #f3f3f3",
    ~borderTop="4px solid #3498db",
    ~borderRadius="50%",
    ()
  ),
  spinKeyframes
);

/* Note: You'll need to define the @keyframes rule in CSS */
let globalCSS = {|
  @keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
  }
|};

[@react.component]
let make = () => {
  <div>
    <style> {React.string(globalCSS)} </style>
    <div style=loadingStyle />
  </div>
}

Responsive Design with Unsafe Escape Hatch

let responsiveStyle = ReactDOMStyle.make(
  ~width="100%",
  ~maxWidth="1200px",
  ~margin="0 auto",
  ~padding="20px",
  ()
);

/* Add media queries using unsafe escape hatch */
let withMediaQueries = ReactDOMStyle.unsafeAddStyle(
  responsiveStyle,
  {
    "@media (max-width: 768px)": {
      "padding": "10px",
      "fontSize": "14px"
    },
    "@media (max-width: 480px)": {
      "padding": "5px",
      "fontSize": "12px"
    }
  }
);

[@react.component]
let make = () => {
  <div style=withMediaQueries>
    {React.string("Responsive content")}
  </div>
}

SVG Styling

let svgStyle = ReactDOMStyle.make(
  ~width="100px",
  ~height="100px",
  ~fill="#3498db",
  ~stroke="#2980b9",
  ~strokeWidth="2",
  ()
);

let pathStyle = ReactDOMStyle.make(
  ~fill="none",
  ~stroke="#e74c3c",
  ~strokeWidth="3",
  ~strokeLinecap="round",
  ~strokeLinejoin="round",
  ()
);

[@react.component]
let make = () => {
  <svg style=svgStyle viewBox="0 0 100 100">
    <circle cx="50" cy="50" r="40" />
    <path 
      style=pathStyle
      d="M30,30 L70,70 M70,30 L30,70"
    />
  </svg>
}