@soyleninjs/postcss-flow
Version: 
PostCSS plugin for creating fluid and responsive CSS values with granular breakpoint control using the flow() function
87 lines (73 loc) • 3.1 kB
JavaScript
// postcss-easy-functions
function flow (decl, definition) {
  // Example: flow(10-360, 20-834, 30-1440-0);
  const breakpoints = definition
    .split(',')
    .map(point => {
      const [value, vw, isFluid] = point.trim().split('-').map(Number)
      return { value, vw, isFluid: isFluid === undefined }
    })
    .sort((a, b) => b.vw - a.vw)
  const property = decl.prop
  const selector = decl.parent.selector
  let cssProperty = ''
  let cssMediaQueries = ''
  const setProperty = (value) => {
    cssProperty += `${property}: ${value}\n`
  }
  const setMediaQuery = (value, vw) => {
    cssMediaQueries += `@media (width <= ${vw}px) { ${selector} {\n`
    cssMediaQueries += `${property}: ${value}\n`
    cssMediaQueries += '}}\n'
  }
  if (breakpoints.length > 1) {
    breakpoints.forEach((point, index) => {
      // Si el punto actual es el ultimo y el punto anterior es fluido, no hacer nada
      if (index === breakpoints.length - 1 && breakpoints[index - 1].isFluid) return
      // Si el punto anterior es fluido y el punto actual no es fluido, no hacer nada
      if (index > 0 && breakpoints[index - 1].isFluid && !point.isFluid) return
      const nextBreakpoint = breakpoints[index + 1]
      if (nextBreakpoint) {
        const minValue = nextBreakpoint.value
        const maxValue = point.value
        const minViewportWidth = nextBreakpoint.vw
        const maxViewportWidth = point.vw
        if (!point.isFluid) {
          const value = `${point.value}px;`
          index > 0 ? setMediaQuery(value, point.vw) : setProperty(value)
        } else {
          if (minValue > maxValue) {
            const value = `clamp(${maxValue}px, calc(${maxValue}px + (${minValue} - ${maxValue}) * ((100vw - ${maxViewportWidth}px) / ${minViewportWidth - maxViewportWidth})), ${minValue}px);`
            index > 0 ? setMediaQuery(value, point.vw) : setProperty(value)
          } else {
            const value = `clamp(${minValue}px, calc(${minValue}px + (${maxValue} - ${minValue}) * ((100vw - ${minViewportWidth}px) / ${maxViewportWidth - minViewportWidth})), ${maxValue}px);`
            index > 0 ? setMediaQuery(value, point.vw) : setProperty(value)
          }
        }
      } else {
        const value = `${point.value}px;`
        index > 0 ? setMediaQuery(value, point.vw) : setProperty(value)
      }
    })
  } else {
    setProperty(`${breakpoints[0].value}px;`)
  }
  // return css
  return { cssProperty, cssMediaQueries }
}
module.exports = (opts = {}) => {
  return {
    postcssPlugin: 'postcss-flow',
    Declaration (decl) {
      const match = decl.value.match(/^flow\((.+)\)$/)
      if (match) {
        const { cssProperty, cssMediaQueries } = flow(decl, match[1])
        // Primero hacer lo que se tenga que hacer con el parent, por que aqui aun existe mi propiedad
        decl.parent.after(cssMediaQueries)
        // Luego reemplazar la propiedad por la nueva, en este punto se remplace la propiedad anterior, por ende "decl" ya no es el mismo de antes!
        decl.replaceWith(cssProperty)
      }
    }
  }
}
module.exports.postcss = true