Reference

불친절한 문서 양해 부탁드려요. 계속해서 업데이트 해나가겠습니다! 😅

import {PrefixRules, Rules} from "./atomizer"
import {cssvar, deg, makeBorder, makeBoxFill, makeColor, makeCommaValues, makeFont, makeFontFamily, makeHBoxFill, makeHBoxWithSemi, makeNumber, makePosition2X, makePosition2Y, makePositionWithSemi, makeRatio, makeSide, makeTextBox, makeTransition, makeValues, makeVBoxFill, makeVBoxWithSemi, percentToEm, px, rpx} from "./makeValue"

export const reset = `
*,:after,:before{margin:0;padding:0;font:inherit;color:inherit;box-sizing:border-box;}
:root{-webkit-tap-highlight-color:transparent;text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.5;overflow-wrap:break-word;word-break:break-word;tab-size:2;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}
html,body{height:100%;}
img,picture,video,canvas{display:block;max-width:100%;}
img{text-indent:-9999px;}
button{background:none;border:0;cursor:pointer;}
a{text-decoration:none;}
table{border-collapse:collapse;border-spacing:0;}
ol,ul,menu,dir{list-style:none;}
*,:after,:before{--w-grow:initial;--w-align:initial;--h-grow:initial;--h-align:initial;}
*,:after,:before{
--a-translate-x:0;
--a-translate-y:0;
--a-rotate:0;
--a-skew-x:0;
--a-skew-y:0;
--a-scale-x:1;
--a-scale-y:1;
--a-transform:translateX(var(--a-translate-x)) translateY(var(--a-translate-y)) rotate(var(--a-rotate)) skewX(var(--a-skew-x)) skewY(var(--a-skew-y)) scaleX(var(--a-scale-x)) scaleY(var(--a-scale-y));
--a-transform3d:translate3d(var(--a-translate-x),var(--a-translate-y),0) rotate(var(--a-rotate)) skewX(var(--a-skew-x)) skewY(var(--a-skew-y)) scaleX(var(--a-scale-x)) scaleY(var(--a-scale-y));
}
`

export const RULES:Rules = {

  // -- Color
  "c": (value:string) => `color:${makeColor(value)};`,
  "color": (value:string) => RULES.c(value),
  "caret": (value:string) => `caret-color:${makeColor(value)};`,
  "caret-current": () => `color:currentColor;`,

  // -- Typography
  "font": (value:string) => makeFont(value),
  "font-size": (value:string) => `font-size:${px(value)};`,
  "line-height": (value:string) => `line-height:${+value < 4 ? makeNumber(+value) : px(value)};`,
  "letter-spacing": (value:string) => `letter-spacing:${percentToEm(value)};`,
  "word-spacing": (value:string) => `word-spacing:${px(value)};`,

  // Font-Family @TODO:font-stack은 일반적인 스택 만들어 두기...(L),Roboto,NotoSans와 같은것도 만들까?
  // @TODO: Font-Family Utility
  "AppleSD": () => `font-family:"Apple SD Gothic Neo";`,
  "Roboto": () => makeFontFamily("Roboto"),

  // @TODO:font-family:var(--serif),serif; 이게 먹히나?
  "sans": () => makeFontFamily("sans"),
  "sans-serif": () => makeFontFamily("sans-serif"),
  "serif": () => makeFontFamily("serif"),
  "cursive": () => makeFontFamily("cursive"),
  "fantasy": () => makeFontFamily("fantasy"),
  "system-ui": () => makeFontFamily("system-ui"),
  "monospace": (value) => {
    if (value === "number") return `font-variant-numeric:tabular-nums;`
    return makeFontFamily("monospace")
  },

  // Font Weight
  "100": () => `font-weight:100;`,
  "200": () => `font-weight:200;`,
  "300": () => `font-weight:300;`,
  "400": () => `font-weight:400;`,
  "500": () => `font-weight:500;`,
  "600": () => `font-weight:600;`,
  "700": () => `font-weight:700;`,
  "800": () => `font-weight:800;`,
  "900": () => `font-weight:900;`,

  "thin": () => `font-weight:200;`,
  "light": () => `font-weight:300;`,
  "regular": () => `font-weight:normal;`,
  "medium": () => `font-weight:500;`,
  "semibold": () => `font-weight:600;`,
  "bold": () => `font-weight:bold;`,
  "heavy": () => `font-weight:900;`,

  // Font Weight Utility
  "thicker": (value = "1") => `text-shadow:0 0 ${px(value)} currentColor;`,

  // Font-Style
  "italic": () => `font-style:italic;`,
  "overline": () => `text-decoration:overline;`,
  "underline": () => `text-decoration:underline;`,
  "line-through": () => `text-decoration:line-through;`,
  "strike": () => `text-decoration:line-through;`,
  "del": () => `text-decoration:line-through;`,

  "small-caps": () => `font-variant-caps:small-caps;`,
  "all-small-caps": () => `font-variant-caps:all-small-caps;`,
  "slashed-zero": () => `font-variant-numeric:slashed-zero;`,
  "tabular-nums": () => `font-variant-numeric:tabular-nums;`,

  "lowercase": () => `text-transform:lowercase;`,
  "uppercase": () => `text-transform:uppercase;`,
  "capitalize": () => `text-transform:capitalize;`,

  // Text Align
  "text": (value:string) => makeTextBox(value),

  "text-justify": () => `text-align:justify;`,
  "text-center": () => `text-align:center;`,
  "text-right": () => `text-align:right;`,
  "text-left": () => `text-align:left;`,

  "vertical-top": () => `vertical-align:top;`,
  "vertical-middle": () => `vertical-align:middle;`,
  "vertical-bottom": () => `vertical-align:bottom;`,
  "sub": () => `vertical-align:sub;`,
  "super": () => `vertical-align:super;`,
  "text-top": () => `vertical-align:text-top;`,
  "text-bottom": () => `vertical-align:text-bottom;`,

  // Text Indent
  "text-indent": (value:string) => `text-indent:${px(value)};`,

  // Text Wrap
  "break-all": () => `word-break:break-all;`,
  "break-word": () => `overflow-wrap:break-word;`,
  "keep-all": () => `word-break:keep-all;`,
  "hyphens": (value = "auto") => `hyphens:${value};`,


  // -- Display
  "block": () => "display:block;",
  "inline-block": () => "display:inline-block;",
  "inline": () => "display:inline;",
  "inline-flex": () => "display:inline-flex;",
  "table": () => "display:table;",
  "inline-table": () => "display:inline-table;",
  "table-caption": () => "display:table-caption;",
  "table-cell": () => "display:table-cell;",
  "table-column": () => "display:table-column;",
  "table-column-group": () => "display:table-column-group;",
  "table-footer-group": () => "display:table-footer-group;",
  "table-header-group": () => "display:table-header-group;",
  "table-row-group": () => "display:table-row-group;",
  "table-row": () => "display:table-row;",
  "flow-root": () => "display:flow-root;",
  "contents": () => "display:contents;",
  "list-item": () => "display:list-item;",

  // -- Box

  // Box-Sizing
  "border-box": () => `box-sizing:border-box;`,
  "content-box": () => `box-sizing:content-box;`,

  // Box-Model
  "w": (value:string) => {
    if (value === "hug") return "width:max-content;"
    if (value === "stretch" || value === "fill") {
      return `&{flex-grow:var(--w-grow);align-self:var(--w-align);flex-shrink:1;max-width:100%}&.h\(fill\),&.h\(stretch\){flex-grow:1;align-self:stretch;}`
    }

    if (value.includes("~")) {
      const result = []

      const values = value.split("~")
      if (values.length <= 2) {
        const [min, max] = values
        min && result.push(`min-width:${px(min)};`)
        max && result.push(`max-width:${px(max)};`)
        return result.join("")
      }

      const [min, width, max] = values
      min && result.push(`min-width:${px(min)};`)
      result.push(`width:${px(width)};`)
      max && result.push(`max-width:${px(max)};`)
      return result.join("")
    }

    return `width:${px(value)};`
  },
  "min-w": (value:string) => `min-width:${px(value)};`,
  "max-w": (value:string) => `max-width:${px(value)};`,

  "h": (value:string) => {
    if (value === "hug") return "height:max-content;"
    if (value === "stretch" || value === "fill") return `flex-grow:var(--h-grow);align-self:var(--h-align)`

    if (value.includes("~")) {
      const result = []

      const values = value.split("~")
      if (values.length <= 2) {
        const [min, max] = value.split("~")
        min && result.push(`min-height:${px(min)};`)
        max && result.push(`max-height:${px(max)};`)
        return result.join("")
      }

      // h(10~20~30)
      const [min, height, max] = values
      min && result.push(`min-height:${px(min)};`)
      result.push(`height:${px(height)};`)
      max && result.push(`max-height:${px(max)};`)
      return result.join("")
    }

    return `height:${px(value)};`
  },
  "min-h": (value:string) => `min-height:${px(value)};`,
  "max-h": (value:string) => `max-height:${px(value)};`,

  // BoxModel - Margin
  "m": (value:string) => `margin:${makeSide(value)};`,
  "mx": (value:string) => `margin-left:${px(value)};margin-right:${px(value)};`,
  "my": (value:string) => `margin-top:${px(value)};margin-bottom:${px(value)};`,
  "mt": (value:string) => `margin-top:${px(value)};`,
  "mr": (value:string) => `margin-right:${px(value)};`,
  "mb": (value:string) => `margin-bottom:${px(value)};`,
  "ml": (value:string) => `margin-left:${px(value)};`,

  // BoxModel - Padding
  "p": (value:string) => `padding:${makeSide(value)};`,
  "px": (value:string) => `padding-left:${px(value)};padding-right:${px(value)};`,
  "py": (value:string) => `padding-top:${px(value)};padding-bottom:${px(value)};`,
  "pt": (value:string) => `padding-top:${px(value)};`,
  "pr": (value:string) => `padding-right:${px(value)};`,
  "pb": (value:string) => `padding-bottom:${px(value)};`,
  "pl": (value:string) => `padding-left:${px(value)};`,

  // BoxModel - Border
  "no-border": () => `border:none;outline:none;`,
  "b": (value:string) => `border:${makeBorder(value)};`,
  "bx": (value:string) => `border-left:${makeBorder(value)};border-right:${makeBorder(value)};`,
  "by": (value:string) => `border-top:${makeBorder(value)};border-bottom:${makeBorder(value)};`,
  "bt": (value:string) => `border-top:${makeBorder(value)};`,
  "br": (value:string) => `border-right:${makeBorder(value)};`,
  "bb": (value:string) => `border-bottom:${makeBorder(value)};`,
  "bl": (value:string) => `border-left:${makeBorder(value)};`,

  "bw": (value:string) => `border-width:${makeValues(value, px)};`,
  "bxw": (value:string) => `border-left-width:${px(value)};border-right-width:${px(value)};`,
  "byw": (value:string) => `border-top-width:${px(value)};border-bottom-width:${px(value)};`,
  "btw": (value:string) => `border-top-width:${px(value)};`,
  "brw": (value:string) => `border-right-width:${px(value)};`,
  "bbw": (value:string) => `border-bottom-width:${px(value)};`,
  "blw": (value:string) => `border-left-width:${px(value)};`,

  "bs": (value:string) => `border-style:${makeValues(value)};`,
  "bxs": (value:string) => `border-left-style:${cssvar(value)};border-right-style:${cssvar(value)};`,
  "bys": (value:string) => `border-top-style:${cssvar(value)};border-bottom-style:${cssvar(value)};`,
  "bts": (value:string) => `border-top-style:${cssvar(value)};`,
  "brs": (value:string) => `border-right-style:${cssvar(value)};`,
  "bbs": (value:string) => `border-bottom-style:${cssvar(value)};`,
  "bls": (value:string) => `border-left-style:${cssvar(value)};`,

  "bc": (value:string) => `border-color:${makeValues(value, makeColor)};`,
  "bxc": (value:string) => `border-left-color:${makeColor(value)};border-right-color:${makeColor(value)};`,
  "byc": (value:string) => `border-top-color:${makeColor(value)};border-bottom-color:${makeColor(value)};`,
  "btc": (value:string) => `border-top-color:${makeColor(value)};`,
  "brc": (value:string) => `border-right-color:${makeColor(value)};`,
  "bbc": (value:string) => `border-bottom-color:${makeColor(value)};`,
  "blc": (value:string) => `border-left-color:${makeColor(value)};`,

  // outline
  "outline": (value:string) => `outline:${makeBorder(value)};`,
  "guide": (value = "#4f80ff") => `&,&>*{outline:1px solid ${makeColor(value)};}`,

  // border-radius
  "r": (value:string) => `border-radius:${makeValues(value, rpx)};`,

  "rt": (value:string) => `border-top-left-radius:${rpx(value)};border-top-right-radius:${rpx(value)};`,
  "rr": (value:string) => `border-top-right-radius:${rpx(value)};border-bottom-right-radius:${rpx(value)};`,
  "rb": (value:string) => `border-bottom-left-radius:${rpx(value)};border-bottom-right-radius:${rpx(value)};`,
  "rl": (value:string) => `border-top-left-radius:${rpx(value)};border-bottom-left-radius:${rpx(value)};`,

  "rtl": (value:string) => `border-top-left-radius:${rpx(value)};`,
  "rtr": (value:string) => `border-top-right-radius:${rpx(value)};`,
  "rbr": (value:string) => `border-bottom-right-radius:${rpx(value)};`,
  "rbl": (value:string) => `border-bottom-left-radius:${rpx(value)};`,

  // box-shadow
  "ring": (value:string) => {
    const [color, size = 1] = value.split("/")
    return `box-shadow:0 0 0 ${px(size)} ${makeColor(color)};`
  },

  "box-shadow": (value:string) => `box-shadow:${makeValues(value, v => Number.isInteger(+v) ? px(v) : cssvar(v))};`,

  // -- Background
  "bg": (value:string) => {
    if (value.startsWith("linear-gradient")) return `background:${value.replace(///g, " ")};`
    if (value.startsWith("radial-gradient")) return `background:${value.replace(///g, " ")};`

    // background-image-url
    if (value.startsWith("url")) return `background-image:${value};`
    if (/^(http|[./])/.test(value)) return `background-image:url(${value});`

    if (value === "transparent") return `background-color:transparent;`
    return `background-color:${makeColor(value)};`
  },

  "bg-image": (value:string) => {
    if (value.startsWith("url")) return `background-image:${value};`
    return `background-image:url(${value});`
  },
  "background-image": (value:string) => RULES["bg-image"](value),

  "bg-position": (value:string) => `background-position:${makeValues(value)};`,

  // @TODO:background 이미지에 대한 세련된 방법이 필요하다!
  "bg-repeat-x": () => `background-repeat:repeat-x;`,
  "bg-repeat-y": () => `background-repeat:repeat-y;`,
  "bg-no-repeat": () => `background-repeat:no-repeat;`,
  "bg-fixed": () => `background-attachment:fixed;`,
  "bg-scroll": () => `background-attachment:scroll;`,

  "contain": () => `background-size:contain;background-position:center;background-repeat:no-repeat;object-fit:contain;`,
  "cover": () => `background-size:cover;background-position:center;background-repeat:no-repeat;object-fit:cover;`,

  /// -- Overflow

  // OverFlow
  "clip": () => `&{overflow:hidden;}&:has(.nowrap\.\.\.){flex-shrink:1;}`,
  "overflow": (value:string) => `overflow:${value};`,
  "overflow-x": (value:string) => `overflow-x:${value};`,
  "overflow-y": (value:string) => `overflow-y:${value};`,

  // Scroll
  "scroll": () => `overflow:auto;`,
  "scroll-x": () => `overflow-x:auto;overflow-y:hidden;`,
  "scroll-y": () => `overflow-x:hidden;overflow-y:auto;`,
  "scrollbar": () => `&{overflow:scroll;}&.scroll{overflow:scroll;}&.scroll-x{overflow-x:scroll;}&.scroll-y{overflow-y:scroll;}`,
  "no-scrollbar": () => `&::-webkit-scrollbar{display:none;}`,
  "no-scrollbar-x": () => `&::-webkit-scrollbar:horizontal{display:none;}`,

  // Scroll Snap
  "scroll-m": (value:string) => `scroll-margin:${makeSide(value)};`,
  "scroll-mt": (value:string) => `scroll-margin-top:${px(value)};`,
  "scroll-mr": (value:string) => `scroll-margin-right:${px(value)};`,
  "scroll-mb": (value:string) => `scroll-margin-bottom:${px(value)};`,
  "scroll-ml": (value:string) => `scroll-margin-left:${px(value)};`,

  "scroll-p": (value:string) => `scroll-padding:${makeSide(value)};`,
  "scroll-pt": (value:string) => `scroll-padding-top:${px(value)};`,
  "scroll-pr": (value:string) => `scroll-padding-right:${px(value)};`,
  "scroll-pb": (value:string) => `scroll-padding-bottom:${px(value)};`,
  "scroll-pl": (value:string) => `scroll-padding-left:${px(value)};`,

  "snap": (value:string) => `scroll-snap-align:${cssvar(value)};`,
  "snap-start": () => `scroll-snap-align:start;`,
  "snap-end": () => `scroll-snap-align:end;`,
  "snap-center": () => `scroll-snap-align:center;`,
  "snap-align-none": () => `scroll-snap-align:none;`,

  "snap-none": () => `scroll-snap-type:none;`,
  "snap-x": () => `scroll-snap-type:x var(--a-scroll-snap-strictness, mandatory);`,
  "snap-x-proximity": () => `scroll-snap-type:x proximity;`,
  "snap-y": () => `scroll-snap-type:y var(--a-scroll-snap-strictness, mandatory);`,
  "snap-y-proximity": () => `scroll-snap-type:y proximity;`,
  "snap-both": () => `scroll-snap-type:both var(--a-scroll-snap-strictness, mandatory);`,
  "snap-both-proximity": () => `scroll-snap-type:both proximity;`,
  "snap-mandatory": () => `--a-scroll-snap-strictness:mandatory;`,
  "snap-proximity": () => `--a-scroll-snap-strictness:proximity;`,

  "snap-normal": () => `scroll-snap-stop:normal;`,
  "snap-always": () => `scroll-snap-stop:always;`,

  // @TODO:- TBD
  "overscroll": (value:string) => `overscroll-behavior:${value};`,
  "overscroll-x": (value:string) => `overscroll-behavior-x:${value};`,
  "overscroll-y": (value:string) => `overscroll-behavior-y:${value};`,

  // @TODO:- TBD
  "no-bouncing": () => "",
  "no-overscroll": () => "",

  // OverFlow + Text
  "white-space-normal": () => `white-space:normal;`,
  "pre": () => `white-space:pre-wrap;`,
  "pre-wrap": () => `white-space:pre-wrap;`,
  "pre-line": () => `white-space:pre-line;`,
  "break-spaces": () => `white-space:break-spaces;`,
  "nowrap": () => `white-space:nowrap;`,
  "nowrap...": () => `white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex-shrink:1;max-width:100%;`,

  // line-clamp vs max-lines
  // @NOTE:일단 기존 프로퍼티에 의거한다는 원칙에따라 line-clamp를 쓴다. 이후 max-lines가 정식 스펙이 되면 deprecated한다.
  // @NOTE:그냥 둘다 제공한다.
  "line-clamp": (value:string) => `display:-webkit-box;-webkit-line-clamp:${value};-webkit-box-orient:vertical;overflow:hidden;`,
  "max-lines": (value:string) => `display:-webkit-box;-webkit-line-clamp:${value};-webkit-box-orient:vertical;overflow:hidden;`,


  // -- Flexbox Layout
  "hbox": (value = "") => `&{display:flex;flex-flow:row;${makeHBoxWithSemi(value)}}${makeHBoxFill()}`,
  "vbox": (value = "") => `&{display:flex;flex-flow:column;${makeVBoxWithSemi(value)}}${makeVBoxFill()}`,
  "wrap": (value = "") => `&{display:flex;flex-flow:wrap;${makeHBoxWithSemi(value)}}${makeHBoxFill()}`,
  "pack": () => `&{display:flex;align-items:center;justify-content:center;}${makeHBoxFill()}`,
  "hpack": () => `&{display:flex;flex-flow:row;align-items:center;justify-content:center;}${makeHBoxFill()}`,
  "vpack": () => `&{display:flex;flex-flow:column;align-items:center;justify-content:center;}${makeVBoxFill()}`,
  "hbox(": () => ``,
  "vbox(": () => ``,
  "subbox": () => `display:flex;flex-flow:inherit;align-items:inherit;justify-content:inherit;`,

  "flex-flow:": (value:string) => `&{flex-flow:${value};}${makeBoxFill(value)}`,
  "flex-direction:": (value:string) => `&{flex-direction:${value};}${makeBoxFill(value)}`,

  "gap": (value:string) => {
    if (value === "auto") return "&{justify-content:space-between;align-content:space-between;}&>:only-child{margin:auto;}"
    return `gap:${makeSide(value)};grid-gap:${makeSide(value)};`
  },

  // @NOTE:IE,safari<=13
  "hgap": (value:string) => `&>*+*{margin-left:${px(value)};}`,
  "hgap-reverse": (value:string) => `&>*+*{margin-right:${px(value)};}`,
  "vgap": (value:string) => `&>*+*{margin-top:${px(value)};}`,
  "vgap-reverse": (value:string) => `&>*+*{margin-bottom:${px(value)};}`,

  // align-items
  "ai": (value:string) => `align-items:${value};`,
  "items": (value:string) => `align-items:${value};`,
  "items-start": () => `align-items:flex-start;`,
  "items-end": () => `align-items:flex-end;`,
  "items-center": () => `align-items:center;`,
  "items-baseline": () => `align-items:baseline;`,
  "items-stretch": () => `align-items:stretch;`,

  // align-content
  "ac": (value:string) => `align-content:${value};`,
  "content-start": () => `align-content:flex-start;`,
  "content-end": () => `align-content:flex-end;`,
  "content-center": () => `align-content:center;`,
  "content-between": () => `align-content:space-between;`,
  "content-around": () => `align-content:space-around;`,
  "content-evenly": () => `align-content:space-evenly;`,
  "content-stretch": () => `align-content:stretch;`,

  // justify-content
  "jc": (value:string) => `justify-content:${value};`,
  "justify": (value:string) => `justify-content:${value};`,
  "justify-start": () => `justify-content:flex-start;`,
  "justify-end": () => `justify-content:flex-end;`,
  "justify-center": () => `justify-content:center;`,
  "justify-between": () => `justify-content:space-between;`,
  "justify-around": () => `justify-content:space-around;`,
  "justify-evenly": () => `justify-content:space-evenly;`,
  "justify-stretch": () => `justify-content:stretch;`,

  "space-between": () => `justify-content:space-between;align-content:space-between;`,
  "space-around": () => `justify-content:space-around;align-content:space-around;`,
  "space-evenly": () => `justify-content:space-evenly;align-content:space-evenly;`,

  // justify-items
  "ji": (value:string) => `justify-items:${value};`,
  "justify-items": (value:string) => `justify-items:${value};`,
  "justify-items-start": () => `justify-items:start;`,
  "justify-items-end": () => `justify-items:end;`,
  "justify-items-center": () => `justify-items:center;`,
  "justify-items-stretch": () => `justify-items:stretch;`,

  // flex: @deprecated
  "flex": (value = "1") => `flex:${makeValues(value)};`,
  "space": (value:string) => `[class*="hbox"]>&{width:${px(value)};}[class*="vbox"]>&{height:${px(value)};}`,

  // flex
  "grow": (value = "1") => `flex-grow:${cssvar(value)};`,
  "grow-0": () => `flex-grow:0;`,
  "no-grow": () => `flex-grow:0;`,
  "shrink": (value = "1") => `flex-shrink:${cssvar(value)};`,
  "no-shrink": () => `flex-shrink:0;`,

  "flex-grow": (value = "1") => `flex-grow:${cssvar(value)};`,
  "flex-shrink": (value = "1") => `flex-shrink:${cssvar(value)};`,
  "flex-basis": (value:string) => `flex-basis:${px(value)};`,

  "flex-wrap": () => "&{flex-wrap:wrap;}:where(&>*){max-width:100%;max-height:100%;}",
  "flex-wrap-reverse": () => "&{flex-wrap:wrap-reverse;}:where(&>*){max-width:100%;max-height:100%;}",
  "flex-nowrap": () => "flex-wrap:nowrap;",
  "order": (value:string) => `order:${cssvar(value)};`,


  // -- Grid
  // @TODO:-- GRID TBD
  "grid": (value) => {
    const css = ["display:grid;"]
    if (+value === +value) css.push(`grid-template-columns:repeat(${value},1fr);`)
    else if (value) css.push(`grid-template-columns:${value};`)
    return css.join("")
  },
  "grid-cols": (value) => {
    const css = ["display:grid;"]
    if (+value === +value) css.push(`grid-template-columns:repeat(${value},1fr);`)
    else if (value) css.push(`grid-template-columns:${value};`)
    return css.join("")
  },
  "inline-grid": () => "display:inline-grid;",


  // -- Position Utilities
  "layer": (value = "") => {
    const pos = {top: "0", right: "0", bottom: "0", left: "0"}
    const outsides = []
    let outside = false

    value.split("+").forEach(v => {
      const [direction, value = "0"] = v.split(":")
      switch (direction) {
        case "top": {
          pos.top = value
          delete pos.bottom
          outsides.push("top")
          return
        }
        case "right": {
          pos.right = value
          delete pos.left
          outsides.push("right")
          return
        }
        case "bottom": {
          pos.bottom = value
          delete pos.top
          outsides.push("bottom")
          return
        }
        case "left": {
          pos.left = value
          delete pos.right
          outsides.push("left")
          return
        }
        case "outside": {
          outside = true
          return
        }
      }
    })

    if (outside) {
      const revert = (b:string, a:string) => {
        pos[a] = pos[b] === "0" ? "100%" : `calc(100% + ${px(pos[b])})`
        delete pos[b]
      }

      outsides.forEach(direction => {
        switch (direction) {
          case "top":
            return revert("top", "bottom")
          case "right":
            return revert("right", "left")
          case "bottom":
            return revert("bottom", "top")
          case "left":
            return revert("left", "right")
        }
      })
    }

    return `position:absolute;` + Object.keys(pos).map((value:string) => `${value}:${px(pos[value])};`).join("")
  },

  "absolute": (value:string) => `position:absolute;${makePositionWithSemi(value)}`,
  "relative": (value:string) => `position:relative;${makePositionWithSemi(value)}`,
  "sticky": (value:string) => `position:sticky;${makePositionWithSemi(value)}`,
  "sticky-top": (value = "0") => `position:sticky;top:${px(value)};`,
  "sticky-right": (value = "0") => `position:sticky;right:${px(value)};`,
  "sticky-bottom": (value = "0") => `position:sticky;bottom:${px(value)};`,
  "sticky-left": (value = "0") => `position:sticky;left:${px(value)};`,
  "fixed": (value:string) => `position:fixed;${makePositionWithSemi(value)}`,
  "static": () => `position:static;`,

  // Position
  "top": (value:string) => `top:${px(value)};`,
  "left": (value:string) => `left:${px(value)};`,
  "right": (value:string) => `right:${px(value)};`,
  "bottom": (value:string) => `bottom:${px(value)};`,

  "x": (value:string) => makePosition2X(value),
  "y": (value:string) => makePosition2Y(value),
  "z": (value:string) => `z-index:${cssvar(value)};`,

  "isolate": () => `isolation:isolate;`,


  // Visibility
  "none": () => `display:none;`,
  "hidden": () => `visibility:hidden;`,
  "invisible": () => `visibility:hidden;`,
  "blind": () => `position:absolute;width:1px;height:1px;padding:0;border:0;margin:-1px;white-space:nowrap;overflow:hidden;clip-path:inset(100%);`,
  "sr-only": () => `position:absolute;width:1px;height:1px;padding:0;border:0;margin:-1px;white-space:nowrap;overflow:hidden;clip-path:inset(100%);`,
  "gone": () => `position:absolute;width:1px;height:1px;padding:0;border:0;margin:-1px;white-space:nowrap;overflow:hidden;clip-path:inset(100%);`,
  "visible": () => `visibility:visible;`,
  "collapse": () => `visibility:collapse;`,
  "opacity": (value:string) => `opacity:${cssvar(value)};`,

  // Interactions
  "col-resize": () => `cursor:col-resize;`,
  "crosshair": () => `cursor:crosshair;`,
  "e-resize": () => `cursor:e-resize;`,
  "ew-resize": () => `cursor:ew-resize;`,
  "grab": () => `&{cursor:grab;}&:active{cursor:grabbing;}`,
  "grabbing": () => `cursor:grabbing;`,
  "n-resize": () => `cursor:n-resize;`,
  "ne-resize": () => `cursor:ne-resize;`,
  "nesw-resize": () => `cursor:nesw-resize;`,
  "ns-resize": () => `cursor:ns-resize;`,
  "nw-resize": () => `cursor:nw-resize;`,
  "nwse-resize": () => `cursor:nwse-resize;`,
  "not-allowed": () => `cursor:not-allowed;`,
  "pointer": () => `cursor:pointer;`,
  "progress": () => `cursor:progress;`,
  "row-resize": () => `cursor:row-resize;`,
  "s-resize": () => `cursor:s-resize;`,
  "se-resize": () => `cursor:se-resize;`,
  "sw-resize": () => `cursor:sw-resize;`,
  "w-resize": () => `cursor:w-resize;`,
  "zoom-in": () => `cursor:zoom-in;`,
  "zoom-out": () => `cursor:zoom-out;`,
  "cursor": (value:string) => `cursor:${value};`,

  "user-select-none": () => "user-select:none;-webkit-user-select:none;",
  "user-select-all": () => "user-select:all;-webkit-user-select:all;",
  "user-select-auto": () => "user-select:auto;-webkit-user-select:auto;",
  "user-select-text": () => "user-select:text;-webkit-user-select:text;",
  "user-select": (value:string) => `user-select:${cssvar(value)};-webkit-user-select:${cssvar(value)};`,

  "pointer-events-none": () => "pointer-events:none;",
  "pointer-events-auto": () => "pointer-events:auto;",

  // 에니메이션:transition(transform=100s/opacity=2s)
  "transition": (value:string) => `transition:${makeTransition(value)};`,

  // transform
  "translate": (value:string) => {
    const [x, y] = makeCommaValues(value, px).split(",");
    return `--a-transform-translate-x:${x};--a-transform-translate-y:${y};transform:var(--a-transform);`;
  },
  "translateX": (value:string) => `--a-translate-x:${px(value)};transform:var(--a-transform);`,
  "translateY": (value:string) => `--a-translate-y:${px(value)};transform:var(--a-transform);`,

  "rotate": (value:string) => {
    let [x, y, z] = makeCommaValues(value, deg).split(",");
    x = x || x
    y = y || x
    z = z || x

    return `--a-rotate:${x};--a-rotate-x:${x};--a-rotate-y:${y};--a-rotate-z:${z};transform:var(--a-transform);`;
  },
  "rotateX": (value:string) => `--a-rotate-x:${deg(value)};transform:var(--a-transform);`,
  "rotateY": (value:string) => `--a-rotate-y:${deg(value)};transform:var(--a-transform);`,

  "scale": (value:string) => {
    let [x, y, z] = makeCommaValues(value).split(",");
    x = x || x
    y = y || x
    z = z || x

    return `--a-scale-x:${x};--a-scale-y:${y};--a-scale-z:${z};transform:var(--a-transform);`;
  },

  "scaleX": (value:string) => `--a-scale-x:${makeNumber(+value)};transform:var(--a-transform);`,
  "scaleY": (value:string) => `--a-scale-y:${makeNumber(+value)};transform:var(--a-transform);`,

  "skew": (value:string) => {
    const [x, y] = makeCommaValues(value, deg).split(",");
    return `--a-skew-x:${x};--a-skew-y:${y};transform:var(--a-transform);`;
  },
  "skewX": (value:string) => `--a-skew-x:${deg(value)};transform:var(--a-transform);`,
  "skewY": (value:string) => `--a-skew-y:${deg(value)};transform:var(--a-transform);`,

  // @TODO: 3d transform
  // "translate3d": (value:string) => `--a-translate-x:${px(value)};--a-translate-y:${px(value)};--a-translate-z:${px(value)};transform:var(--a-transform);`,
  // "rotate3d": (value:string) => `--a-rotate-x:${deg(value)};--a-rotate-y:${deg(value)};--a-rotate-z:${deg(value)};transform:var(--a-transform);`,
  // "translateZ": (value:string) => `--a-translate-z:${px(value)};transform:var(--a-transform);`,
  // "rotateZ": (value:string) => `--a-rotate-z:${deg(value)};transform:var(--a-transform);`,
  // "skewZ": (value:string) => `--a-skew-z:${deg(value)};transform:var(--a-transform);`,
  // "scaleZ": (value:string) => `--a-scale-z:${makeCommaValues(value)};transform:var(--a-transform);`,

  // Util
  "ratio": (value:string) => `&{position:relative;}&:before{content:"";display:block;width:100%;padding-top:${makeRatio(value)};}&>*{position:absolute;top:0;left:0;width:100%;height:100%;}`,
  "aspect": (value:string) => `aspect-ratio:${cssvar(value.replace(/:/g, "/"))};`,
  "aspect-ratio": (value:string) => `aspect-ratio:${cssvar(value.replace(/:/g, "/"))};`,
  "gpu": () => `transform:translateZ(0.1px);`,

  // etc
  "content": (value = "''") => `content:${cssvar(value)};`,
  "app-region": (value:string) => `app-region:${value};-webkit-app-region:${value};`,
  "clip-path": (value:string) => `clip-path:${makeValues(value)};-webkit-clip-path:${makeValues(value)};`,

  // table
  "table-fixed": () => `table-layout:fixed;`,
  "table-auto": () => `table-layout:auto;`,
  "table-layout-fixed": () => `table-layout:fixed;`,
  "table-layout-auto": () => `table-layout:auto;`,

  // Float & Clear
  "float": (value:string) => `float:${cssvar(value)};`,
  "clear": (value:string) => `clear:${cssvar(value)};`,

  // Filter
  "blur": (value:string) => `filter:blur(${px(value)});-webkit-filter:blur(${px(value)});`,
  "brightness": (value:string) => `filter:brightness(${cssvar(value)});-webkit-filter:brightness(${cssvar(value)});`,
  "contrast": (value:string) => `filter:contrast(${cssvar(value)});-webkit-filter:contrast(${cssvar(value)});`,
  "drop-shadow": (value:string) => `filter:drop-shadow(${makeValues(value, px)});-webkit-filter:drop-shadow(${makeValues(value, px)});`,
  "grayscale": (value:string) => `filter:grayscale(${cssvar(value)});-webkit-filter:grayscale(${cssvar(value)});`,
  "hue-rotate": (value:string) => `filter:hue-rotate(${cssvar(value)});-webkit-filter:hue-rotate(${cssvar(value)});`,
  "invert": (value:string) => `filter:invert(${cssvar(value)});-webkit-filter:invert(${cssvar(value)});`,
  "sepia": (value:string) => `filter:sepia(${cssvar(value)});-webkit-filter:sepia(${cssvar(value)});`,
  "saturate": (value:string) => `filter:saturate(${cssvar(value)});-webkit-filter:saturate(${cssvar(value)});`,

  "backdrop-blur": (value:string) => `backdrop-filter:blur(${px(value)});-webkit-backdrop-filter:blur(${px(value)});`,
  "backdrop-brightness": (value:string) => `backdrop-filter:brightness(${cssvar(value)});-webkit-backdrop-filter:brightness(${cssvar(value)});`,
  "backdrop-contrast": (value:string) => `backdrop-filter:contrast(${cssvar(value)});-webkit-backdrop-filter:contrast(${cssvar(value)});`,
  "backdrop-drop-shadow": (value:string) => `backdrop-filter:drop-shadow(${makeValues(value, px)});-webkit-backdrop-filter:drop-shadow(${makeValues(value, px)});`,
  "backdrop-grayscale": (value:string) => `backdrop-filter:grayscale(${cssvar(value)});-webkit-backdrop-filter:grayscale(${cssvar(value)});`,
  "backdrop-hue-rotate": (value:string) => `backdrop-filter:hue-rotate(${cssvar(value)});-webkit-backdrop-filter:hue-rotate(${cssvar(value)});`,
  "backdrop-invert": (value:string) => `backdrop-filter:invert(${cssvar(value)});-webkit-backdrop-filter:invert(${cssvar(value)});`,
  "backdrop-sepia": (value:string) => `backdrop-filter:sepia(${cssvar(value)});-webkit-backdrop-filter:sepia(${cssvar(value)});`,
  "backdrop-saturate": (value:string) => `backdrop-filter:saturate(${cssvar(value)});-webkit-backdrop-filter:saturate(${cssvar(value)});`,

  // @TODO:triangle
  "triangle": (value:string) => {
    const [direction, size, angle = 0] = value.split("/")
    const bd = ["top", "right", "bottom", "left", "top", "right", "bottom", "left"]
    const bdr = bd.slice(bd.indexOf(direction))
    const height = 0.5

    let css = `width:0;height:0;border:0 solid transparent;`
    css += "border-" + bdr[1] + "-width:" + Math.round(+size * (-angle + 1) / 2) + "px;"
    css += "border-" + bdr[3] + "-width:" + Math.round(+size * (+angle + 1) / 2) + "px;"
    css += "border-" + bdr[2] + ":" + Math.round(+size * height) + "px solid black;"

    return css
  },

  // elevation
  "elevation": (value:string) => {
    const dp = +value
    if (!dp) {
      return `box-shadow:none;`
    }

    const blur = (dp == 1 ? 3 : dp * 2)
    const amba = (dp + 10 + (dp / 9.38)) / 100
    const diry = (dp < 10 ? (dp % 2 == 0 ? dp - ((dp / 2) - 1) : (dp - ((dp - 1) / 2))) : dp - 4)
    const dira = (24 - (Math.round(dp / 10))) / 100

    return `box-shadow:0px ${px(dp)} ${px(blur)} rgba(0,0,0,${amba}),0px ${px(diry)} ${px(blur)} rgba(0,0,0,${dira});`
  },
}

// Prefix
// pseudo class
export const PREFIX_PSEUDO_CLASS:PrefixRules = {
  "hover:": {media: `(hover:hover)`, selector: `&:hover,&.\:hover`},
  "active:": {selector: `html &:active,html &.\:active`},
  "focus:": {selector: `html &:focus,html &.\:focus`},
  "focus-visible": {selector: `html &:focus-visible,html &.\:focus-visible`},
  "focus-within:": {selector: `html &:focus-within,html &.\:focus-within`},
  "checked:": {selector: `html &:checked,html &.\:checked`},
  "read-only:": {selector: `html &:read-only,html &.\:read-only`},
  "enabled:": {selector: `html &:enabled,html &.\:enabled`},
  "disabled:": {selector: `html body &:disabled,html body &.\:disabled,html body &[disabled]`},

  "group-hover:": {selector: `.group:hover &,html .group.\:hover &`},
  "group-active:": {selector: `html .group:active &,html .group.\:active &`},
  "group-focus:": {selector: `html .group:focus &,html .group.\:focus &`},
  "group-focus-within:": {selector: `html .group:focus-within &,html .group\:focus-within`},
  "group-checked:": {selector: `html .group:checked &,html .group.\:checked &`},
  "group-read-only:": {selector: `html .group:read-only &,html .group.\:read-only &`},
  "group-enabled:": {selector: `html .group:enabled &,html .group.\:enabled &`},
  "group-disabled:": {selector: `html body .group:disabled &,html body .group[disabled] &,html body .group.disabled &`},

  "placeholder:": {selector: `&::placeholder`},

  "odd:": {selector: `&:nth-child(2n+1)`},
  "even:": {selector: `&:nth-child(2n)`},

  "first:": {selector: `&:first-child`},
  "last:": {selector: `&:last-child`},

  "after:": {selector: `&::after`},
  "before:": {selector: `&::before`},

  "selection::": {selector: `&::selection,& *::selection`},
}


// media query
export const PREFIX_MEDIA_QUERY:PrefixRules = {
  "sm:": {media: `(min-width:480px)`, selector: `html &`},
  "md:": {media: `(min-width:768px)`, selector: `html &`},
  "lg:": {media: `(min-width:1024px)`, selector: `html &`},
  "xl:": {media: `(min-width:1280px)`, selector: `html &`},

  "sm~:": {media: `(min-width:480px)`, selector: `html &`},
  "md~:": {media: `(min-width:768px)`, selector: `html &`},
  "lg~:": {media: `(min-width:1024px)`, selector: `html &`},
  "xl~:": {media: `(min-width:1280px)`, selector: `html &`},

  "~sm:": {media: `(max-width:479.98px)`, selector: `html &`},
  "~md:": {media: `(max-width:767.98px)`, selector: `html &`},
  "~lg:": {media: `(max-width:1023.98px)`, selector: `html &`},
  "~xl:": {media: `(max-width:1279.98px)`, selector: `html &`},

  "mobile:": {media: `(max-device-width:767.98px)`, selector: `html &`},
  "tablet:": {media: `(min-device-width:768px) and (max-device-width:1023.98px)`, selector: `html &`},
  "desktop:": {media: `(min-device-width:1024px)`, selector: `html &`},
  "!mobile:": {media: `(min-device-width:768px)`, selector: `html &`},
  "!desktop:": {media: `(max-device-width:1023.98px)`, selector: `html &`},

  // "touch:":{media:`(hover:none)`,selector:`html &`},
  // "!touch:":{media:`(hover:hover)`,selector:`html &`},

  // @TBD: don't use it!
  "touch:": {media: `(max-device-width:1023.98px)`, selector: `html &`},
  "!touch:": {media: `(min-device-width:1024px)`, selector: `html &`},

  "portrait:": {media: `(orientation:portrait)`, selector: `html &`},
  "landscape:": {media: `(orientation:landscape)`, selector: `html &`},

  "print:": {media: `print`, selector: `html &`},
  "screen:": {media: `screen`, selector: `html &`},
  "speech:": {media: `speech`, selector: `html &`},

  // dark:@TBD
  "dark:": {selector: `@media(prefers-color-scheme:dark){html &{...}}\nhtml.dark &{...}`},
}

export const AT_RULE = {
  "@w": (ident:string, tokens:Array<{type:string, value:string}>) => {
    if (tokens[2]?.value !== "(" || tokens[tokens.length - 1]?.value !== ")") {
      throw Error("invalid syntax!")
    }

    const value = tokens.slice(3, -1).map(t => t.value).join("")
    if (!value.includes("~")) {
      throw Error("invalid syntax! required '~'.")
    }

    let [min, max] = value.split("~")

    if (min) min = `(min-width:${px(+min)})`
    if (max) max = `(max-width:${px(+max - 0.02)})`
    const rule = [min, max].filter(Boolean).join(" and ")

    return {media: ` only screen and ${rule}`, selector: `html &`}
  }
}

// selector
export const PREFIX_SELECTOR:Record<string, (selector:string) => string> = {
  ">>": (selector:string) => `& ${selector.slice(2)}`,
  "&:": (selector:string) => `${selector}`,
  "&.": (selector:string) => `${selector}`,
  "&[": (selector:string) => `${selector}`,
  ".": (selector:string) => `&${selector},${selector} &`,
  "[": (selector:string) => `&${selector},${selector} &`,
  ">": (selector:string) => `&${selector}`,
  "+": (selector:string) => `&${selector}`,
  "#": (selector:string) => `&${selector}`,
}

// "~": (selector:string) => `&${selector}`,