Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ LiveStyle provides a type-safe, composable styling system with:
- **Deterministic hashing**: Same styles always produce same class names
- **CSS Variables**: Type-safe design tokens with `vars/1`
- **Constants**: Static values inlined at compile time with `consts/1`
- **Theming**: Override variables with `theme/2`
- **Theming**: Override CSS variables with `theme_class/2`
- **@layer support**: CSS cascade layers for predictable specificity
- **Last-wins merging**: Like StyleX, later styles override earlier ones

Expand All @@ -19,7 +19,7 @@ Add `live_style` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:live_style, "~> 0.14.0"}
{:live_style, "~> 0.16.2"}
]
end
```
Expand Down Expand Up @@ -174,7 +174,7 @@ LiveStyle brings Meta's StyleX philosophy to Phoenix LiveView:
```elixir
def deps do
[
{:live_style, "~> 0.14.0"},
{:live_style, "~> 0.16.2"},
# Automatic vendor prefixing
{:autoprefixer_ex, "~> 0.1.0"},
# Deprecation warnings for CSS properties
Expand Down
19 changes: 12 additions & 7 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import Config

config :git_ops,
mix_project: LiveStyle.MixProject,
changelog_file: "CHANGELOG.md",
repository_url: "https://github.com/lifeiscontent/live_style",
manage_mix_version?: true,
manage_readme_version?: "README.md",
version_tag_prefix: "v"
if config_env() == :dev do
config :git_ops,
mix_project: LiveStyle.MixProject,
changelog_file: "CHANGELOG.md",
repository_url: "https://github.com/lifeiscontent/live_style",
manage_mix_version?: true,
manage_readme_version: "README.md",
managed_files: [
{"guides/getting-started.md", :string}
],
version_tag_prefix: "v"
end
34 changes: 20 additions & 14 deletions guides/advanced-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule MyAppWeb.Card do

def render(assigns) do
~H"""
<div class={LiveStyle.default_marker()}>
<div {css([LiveStyle.default_marker()])}>
<div {css(:card_content)}>
Hover the parent to move me
</div>
Expand Down Expand Up @@ -58,8 +58,7 @@ defmodule MyAppWeb.Table do
use LiveStyle
alias LiveStyle.When

@row_marker LiveStyle.marker(:row)
@row_hover When.ancestor(":hover", @row_marker)
@row_hover When.ancestor(":hover", marker(:row))

class :cell,
opacity: [
Expand All @@ -75,9 +74,9 @@ defmodule MyAppWeb.Table do

def render(assigns) do
~H"""
<div class={LiveStyle.default_marker()}>
<div {css([LiveStyle.default_marker()])}>
<table>
<tr :for={row <- @rows} class={@row_marker}>
<tr :for={row <- @rows} {css([marker(:row)])}>
<td :for={cell <- row} {css(:cell)}>
<%= cell %>
</td>
Expand Down Expand Up @@ -278,7 +277,7 @@ export function createViewTransitionDom(options = {}) {
types: transitionTypes.length ? transitionTypes : ["same-document"],
})
} catch (error) {
// Firefox 144+ doesn't support callbackOptions yet
// Some browsers do not support callbackOptions yet.
document.startViewTransition(update)
}
},
Expand Down Expand Up @@ -483,7 +482,10 @@ end

### Browser Support

View Transitions are supported in Chrome 111+, Edge 111+, Safari 18+, and Firefox 144+. They gracefully degrade in unsupported browsers.
View Transitions are available in current major browsers, but support details
continue to move as the API evolves. The adapter above checks
`document.startViewTransition`, so unsupported browsers render the update
without an animated transition.

## Scroll-Driven Animations

Expand Down Expand Up @@ -637,7 +639,11 @@ Range keywords:

### Browser Support

Scroll-driven animations are supported in Chrome 115+, Edge 115+, and Safari 18+. They require no JavaScript - the browser handles all animation timing based on scroll position.
Scroll-driven animation support is still uneven across widely used browsers.
Treat these as progressive enhancements and consider wrapping critical effects in
`@supports (animation-timeline: scroll())`. They require no JavaScript in
supporting browsers - the browser handles animation timing based on scroll
position.

## CSS Anchor Positioning

Expand Down Expand Up @@ -717,7 +723,9 @@ Only positioning-related properties are allowed in `position_try`:

### Browser Support

CSS Anchor Positioning is available in Chromium 125+ (June 2024). Firefox and Safari don't yet support this feature. Consider feature detection or fallback positioning.
CSS Anchor Positioning is newly available across current major browser engines,
but older browser versions still need fallback positioning. Consider feature
detection when the anchored UI is critical.

## Combining Features

Expand All @@ -729,26 +737,24 @@ defmodule MyAppWeb.Dropdown do
use LiveStyle
alias LiveStyle.When

@trigger_marker LiveStyle.marker(:trigger)

class :menu,
position: "absolute",
position_anchor: "--dropdown-trigger",
top: "anchor(bottom)",
opacity: [
{:default, "0"},
{When.sibling_before(":focus", @trigger_marker), "1"}
{When.sibling_before(":focus", marker(:trigger)), "1"}
],
transform: [
{:default, "translateY(-10px)"},
{When.sibling_before(":focus", @trigger_marker), "translateY(0)"}
{When.sibling_before(":focus", marker(:trigger)), "translateY(0)"}
],
transition: "opacity 200ms, transform 200ms"

def dropdown(assigns) do
~H"""
<div>
<button class={[@trigger_marker]} style="anchor-name: --dropdown-trigger">
<button {css([marker(:trigger)])} style="anchor-name: --dropdown-trigger">
Menu
</button>
<div {css(:menu)}>
Expand Down
14 changes: 12 additions & 2 deletions guides/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,20 @@ end

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `manifest_path` | string | `_build/{env}/live_style/{app}/manifest.etf` | Override where compiled style data is stored |
| `usage_path` | string | `_build/{env}/live_style/{app}/usage.etf` | Override where class usage data is stored |
| `shorthand_behavior` | atom | `:accept_shorthands` | How to handle CSS shorthands |
| `class_name_prefix` | string | `"x"` | Prefix for generated classes, variables, and keyframes |
| `debug_class_names` | boolean | `false` | Include property names in generated class names |
| `font_size_px_to_rem` | boolean | `false` | Convert numeric `font_size` px values to rem |
| `font_size_root_px` | number | `16` | Root pixel size used for px-to-rem conversion |
| `use_css_layers` | boolean | `false` | Use CSS `@layer` for specificity |
| `prefix_css` | mfa | `nil` | Vendor prefixing function |
| `deprecated?` | mfa | `nil` | Deprecation check function |
| `validate_properties` | boolean | `true` | Validate CSS property names at compile time |
| `unknown_property_level` | atom | `:warn` | `:warn`, `:error`, or `:ignore` for unknown CSS properties |
| `vendor_prefix_level` | atom | `:warn` | `:warn` or `:ignore` for unnecessary vendor-prefixed properties |
| `deprecated_property_level` | atom | `:warn` | `:warn` or `:ignore` for deprecated properties |
| `prefix_css` | function or MFA | `nil` | Vendor prefixing function |
| `deprecated?` | function or MFA | `nil` | Deprecation check function |

### Profile Options

Expand Down
43 changes: 27 additions & 16 deletions guides/design-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ defmodule MyAppWeb.Breakpoints do
use LiveStyle

consts [
sm: "@media (min-width: 640px)",
md: "@media (min-width: 768px)",
lg: "@media (min-width: 1024px)",
xl: "@media (min-width: 1280px)"
sm: "(min-width: 640px)",
md: "(min-width: 768px)",
lg: "(min-width: 1024px)",
xl: "(min-width: 1280px)"
]
end

Expand Down Expand Up @@ -235,28 +235,39 @@ end
| Function | CSS Syntax |
|----------|------------|
| `color/1` | `<color>` |
| `length/1` | `<length>` |
| `angle/1` | `<angle>` |
| `custom/2` | custom syntax |
| `custom_ident/1` | `<custom-ident>` |
| `image/1` | `<image>` |
| `integer/1` | `<integer>` |
| `length/1` | `<length>` |
| `length_percentage/1` | `<length-percentage>` |
| `no_inherit/1` | sets `inherits: false` |
| `number/1` | `<number>` |
| `time/1` | `<time>` |
| `percentage/1` | `<percentage>` |
| `resolution/1` | `<resolution>` |
| `string/1` | `<string>` |
| `time/1` | `<time>` |
| `transform_function/1` | `<transform-function>` |
| `transform_list/1` | `<transform-list>` |
| `url/1` | `<url>` |
| `any/1` | `*` |

## Recommended Token Structure

A recommended structure for larger applications:

```
lib/my_app/tokens/
├── colors.ex # MyAppWeb.Colors - raw color palette
├── semantic.ex # MyAppWeb.Semantic - themed semantic tokens
├── spacing.ex # MyAppWeb.Spacing - spacing scale
├── font_size.ex # MyAppWeb.FontSize - typography sizes
├── radius.ex # MyAppWeb.Radius - border radii
├── shadow.ex # MyAppWeb.Shadow - box shadows
├── breakpoints.ex # MyAppWeb.Breakpoints - media queries
├── z_index.ex # MyAppWeb.ZIndex - z-index values
└── animations.ex # MyAppWeb.Animations - keyframes
lib/my_app_web/style/
├── colors.ex # MyAppWeb.Style.Colors - raw color palette
├── semantic.ex # MyAppWeb.Style.Semantic - themed semantic tokens
├── spacing.ex # MyAppWeb.Style.Spacing - spacing scale
├── font_size.ex # MyAppWeb.Style.FontSize - typography sizes
├── radius.ex # MyAppWeb.Style.Radius - border radii
├── shadow.ex # MyAppWeb.Style.Shadow - box shadows
├── breakpoints.ex # MyAppWeb.Style.Breakpoints - media query conditions
├── z_index.ex # MyAppWeb.Style.ZIndex - z-index values
└── animations.ex # MyAppWeb.Style.Animations - keyframes
```

Example Colors module:
Expand Down
10 changes: 5 additions & 5 deletions guides/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ LiveStyle is a compile-time CSS-in-Elixir library for Phoenix.

## Migrating from Tailwind

It's a find-and-replace:
The setup follows the same shape as Tailwind:

| Find | Replace |
|------|---------|
| `@import "tailwindcss"` | `@import "live_style"` |
| `{:tailwind, ...}` | `{:live_style, "~> 0.0"}` |
| `{:tailwind, ...}` | `{:live_style, "~> 0.16.2"}` |
| `config :tailwind, my_app: [...]` | `config :live_style, my_app: [...]` |
| `tailwind my_app` | `live_style my_app` |
| `Tailwind` | `LiveStyle` |
Expand Down Expand Up @@ -38,7 +38,7 @@ end

def deps do
[
{:live_style, "~> 0.0"},
{:live_style, "~> 0.16.2"},
...
]
end
Expand Down Expand Up @@ -70,7 +70,7 @@ watchers: [
]
```

Run `mix deps.get` and you're done.
Run `mix deps.get`, then build assets with your normal Phoenix alias.

### Incremental Migration

Expand All @@ -94,7 +94,7 @@ For new Phoenix projects without Tailwind:
# mix.exs
def deps do
[
{:live_style, "~> 0.0"}
{:live_style, "~> 0.16.2"}
]
end
```
Expand Down
4 changes: 3 additions & 1 deletion guides/styling-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ class :container,
]
```

Using breakpoint constants with string interpolation:
Using breakpoint constants with string interpolation. Define those constants as
media query conditions like `"(min-width: 768px)"`, then add the `@media` prefix
at the call site:

```elixir
class :grid,
Expand Down
12 changes: 5 additions & 7 deletions guides/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,13 @@ Apply the theme at the root level:

### JavaScript Integration

Since theme class names are generated at compile time, you need to bridge them to JavaScript for runtime theme switching. Use data attributes to pass the class names:
Since theme class names are generated at compile time, you need to bridge them to JavaScript for runtime theme switching. Use a data attribute to pass the dark theme class. The light theme is the default `vars` output, so clearing theme classes restores it:

```heex
<!-- In root.html.heex -->
<html
lang="en"
data-theme-dark={theme_class({MyAppWeb.Semantic, :dark})}
data-theme-light={theme_class({MyAppWeb.Semantic, :light})}
>
```

Expand All @@ -161,8 +160,7 @@ Then in a blocking `<script>` tag in `<head>` (to prevent flash of unstyled cont
(function() {
const html = document.documentElement;
const themes = {
dark: html.dataset.themeDark,
light: html.dataset.themeLight
dark: html.dataset.themeDark
};

const getStoredTheme = () => localStorage.getItem("theme");
Expand All @@ -176,9 +174,9 @@ Then in a blocking `<script>` tag in `<head>` (to prevent flash of unstyled cont
// Apply the appropriate theme class
if (theme === "system") {
const systemTheme = getSystemTheme();
if (themes[systemTheme]) html.classList.add(themes[systemTheme]);
} else if (themes[theme]) {
html.classList.add(themes[theme]);
if (systemTheme === "dark" && themes.dark) html.classList.add(themes.dark);
} else if (theme === "dark" && themes.dark) {
html.classList.add(themes.dark);
}
};

Expand Down
Loading