{"componentChunkName":"component---src-templates-post-jsx","path":"/blog/field-sizing-just-works/","result":{"data":{"mdx":{"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nconst _frontmatter = {\n  \"title\": \"Field-sizing just works!\",\n  \"cover\": \"/blogs/field-sizing/cover.svg\",\n  \"date\": \"2024-01-30\",\n  \"updated\": \"2024-04-02\",\n  \"type\": [\"tutorial\"]\n};\nconst layoutProps = {\n  _frontmatter\n};\nconst MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  let {\n    components\n  } = _ref,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `field-sizing`), ` is wild. It's a new CSS property that makes `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `input`), `, `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `textarea`), ` and `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `select`), ` automatically scale to the size of their content.`), mdx(\"p\", null, `That means it replaces a bunch of different techniques that all require JavaScript to some degree and `, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.htmhell.dev/adventcalendar/2023/2/\"\n  }), `I love it when that happens`), `.`), mdx(\"h2\", {\n    \"id\": \"syntax\"\n  }, `Syntax`), mdx(\"p\", null, `field-sizing has two values:`), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"code\", _extends({\n    parentName: \"li\"\n  }, {\n    \"className\": \"language-text\"\n  }), `fixed`), `, which is the current behavior of inputs, textareas and selects where they have a fixed size regardless of the content.`), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"code\", _extends({\n    parentName: \"li\"\n  }, {\n    \"className\": \"language-text\"\n  }), `content`), `, which makes the form element scale to the size of the content`)), mdx(\"p\", null, `When you apply it to an input or select it will scale to the width of the content. When you apply it to a textarea, it will scale to the height of the content.`), mdx(\"h2\", {\n    \"id\": \"a-field-sizing-demo\"\n  }, `A field-sizing demo`), mdx(\"p\", null, `Here are three interactive demos for `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `field-sizing`), `: one for input, one for textarea and one for select. You can change the content and see how the input or textarea scales to the size of the content, and for the select you pick a different option to see it update.`), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `You'll need Polypane or a Chromium based browser`), ` (at least 120 with experimental features turned on, or 123 when it's released later this year) to see this demo. If you don't have Polypane, you can `, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://dashboard.polypane.app/register/\"\n  }), `try it for free`), `.`)), mdx(\"p\", null, `An example using `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `input`), `:`), mdx(FieldSizingExample, {\n    mdxType: \"FieldSizingExample\"\n  }), mdx(\"p\", null, `An example using `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `textarea`), ` (with a fixed width):`), mdx(FieldSizingTextareaExample, {\n    mdxType: \"FieldSizingTextareaExample\"\n  }), mdx(\"p\", null, `And an example using `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `select`), `:`), mdx(FieldSizingSelectExample, {\n    mdxType: \"FieldSizingSelectExample\"\n  }), mdx(\"h2\", {\n    \"id\": \"how-we-use-it\"\n  }, `How we use it`), mdx(\"p\", null, `Polypane has parts of the UI where users can input content that should (up to a certain degree) scale with the content:`), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `The width and height of each pane`)), mdx(\"img\", {\n    src: panesizeimg,\n    className: \"imgshadow\",\n    alt: \"the width and height of two panes highlighted\"\n  }), mdx(\"p\", null, `Having the inputs scale to their content means that they will always look correctly aligned, whether the dimensions are \"320x568 px\" or \"31.25x48 em\".`), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, `The content area in the elements editor`)), mdx(\"img\", {\n    src: contentimg,\n    className: \"imgshadow\",\n    alt: \"The content area in the elements editor\"\n  }), mdx(\"p\", null, `Autoscaling the content area means that you can always see the full content of the element so you're not scolling up and down (up to a certain height of course).`), mdx(\"p\", null, `Polypane has been around for a few years and we actually went through a few different techniques to make both of these work. Let's go through them and see how `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `field-sizing`), ` makes them all obsolete.`), mdx(\"h2\", {\n    \"id\": \"input-listeners-the-all-javascript-way\"\n  }, `Input listeners: the all-JavaScript way`), mdx(\"p\", null, `Being built in React, our first iteration used specific React packages: `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `react-input-autosize`), ` and `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `react-autosize-textarea`), `.`), mdx(\"p\", null, `These libraries automatically resize the input or textarea to the size of the content and also let you set some custom properties to determine the minimum and maximum size. They don't require you to add any custom styling.`), mdx(ReactExamples, {\n    mdxType: \"ReactExamples\"\n  }), mdx(\"p\", null, `There are a number of these types of libraries and they work in one of two ways:`), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, `They render a second element with the same content of the input, measure that size and then set the size of the input to that size.`), mdx(\"li\", {\n    parentName: \"ul\"\n  }, `They measure the scrollHeight or scrollWidth of the element on any change, then set the size of the input to that size.`)), mdx(\"p\", null, `This works, but at the expense of another element and a bunch of listeners that make sure to listen on text input, take the placeholder value into account, handle window resizing, font loading and on a bunch of other events.`), mdx(\"p\", null, `It's a lot of work to make sure it looks good. You'll see how the ones above aren't quite right on first load.`), mdx(\"h2\", {\n    \"id\": \"grid-based-the-semi-javascript-way\"\n  }, `Grid-based: the semi-JavaScript way`), mdx(\"p\", null, `Having all those listeners started to stack up, so I decided to switch out the approach for something that was a bit more performant: using a grid area that contains the input/textarea and replicating the value from it into a pseudo-element.`), mdx(FieldSizingGridExample, {\n    mdxType: \"FieldSizingGridExample\"\n  }), mdx(\"p\", null, `By setting both the textarea and the ::after pseudo element to `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `grid-area: 1/1/2/2`), ` we essentially force them to be the same height. When the `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `::after`), ` pseudo becomes larger, the textarea will automatically size along with it.`), mdx(\"p\", null, `The way the grid area works is that it scales both elements to the larger of the two. By default a textarea starts at two rows high. By adding `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `rows=\"1\"`), ` we make sure that the textarea can shrink down to a single row.`), mdx(\"p\", null, `that `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `::after`), ` pseudo has a `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `content`), ` property that is set to the value of the textarea as well as a single space. This space makes sure that when you enter a newline in the textarea, that newline also causes the textarea to expand.`), mdx(\"p\", null, `With `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `visibility: hidden`), ` we can make sure that while the `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `::after`), ` pseudo does its job of sizing the textarea, it's not visible.`), mdx(\"p\", null, `This is a nice solution because it requires almost no listeners, especially if the form elements are already controlled by React: you only have to render the value into both the input and a data attribute, making it very light-weight on the JavaScript side. Otherwise, you can add one listener to the `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `input`), ` event to replicate the value as you can see in the demo.`), mdx(\"p\", null, `However as you can see on the CSS side, we need a lot more styling to make it work: the `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `::after`), ` pseudo element needs to behave `, mdx(\"em\", {\n    parentName: \"p\"\n  }, `exactly`), ` like the textarea: same font, same white-space handling, same padding etc. If you don't get this right, things can be off by a few pixels, causing lost text, scrollbars or cut off lines.`), mdx(\"h2\", {\n    \"id\": \"the-new-css-only-way\"\n  }, `The New CSS-only way`), mdx(\"p\", null, `Enter `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `field-sizing`), `. It's part of `, mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://drafts.csswg.org/css-ui/#field-sizing\"\n  }), `CSS Basic User Interface Module Level 4`), ` which is still in editor draft state (meaning things can and will change) and for now its a Chromium-only feature. I expect it to land in other browsers sometime this year though, with Safari having alread given a positive signal, and Firefox will probably follow suit.`), mdx(FieldSizingExample, {\n    mdxType: \"FieldSizingExample\"\n  }), mdx(\"p\", null, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `field-sizing`), ` has all the benefits of the grid-based example above without needing the wrapper element, the input listener and all the work of making sure the CSS Styling stays exactly the same. And it works for inputs, textareas and select.`), mdx(\"p\", null, `It's a single CSS property that really does all of that work for you, it even sizes to the placeholder when you clear it. When I was done implementing it (consisting of removing a bunch of code) I was in awe of how easy it was.`), mdx(\"h3\", {\n    \"id\": \"notes\"\n  }, `Notes`), mdx(\"p\", null, `Aside from the lacking browser support, you'll also want to make sure that your input, select or textarea has some boundaries. If you don't, it will just keep growing and growing. You can do this by setting a `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `max-width`), ` or `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `max-height`), ` on the element.`), mdx(\"p\", null, `Likewise, if you don't want it to shrink down to the width of a space or dot, add a `, mdx(\"code\", _extends({\n    parentName: \"p\"\n  }, {\n    \"className\": \"language-text\"\n  }), `min-width`), ` for your inputs and selects.`));\n}\n;\nMDXContent.isMDXComponent = true;","timeToRead":4,"tableOfContents":{"items":[{"url":"#syntax","title":"Syntax"},{"url":"#a-field-sizing-demo","title":"A field-sizing demo"},{"url":"#how-we-use-it","title":"How we use it"},{"url":"#input-listeners-the-all-javascript-way","title":"Input listeners: the all-JavaScript way"},{"url":"#grid-based-the-semi-javascript-way","title":"Grid-based: the semi-JavaScript way"},{"url":"#the-new-css-only-way","title":"The New CSS-only way","items":[{"url":"#notes","title":"Notes"}]}]},"excerpt":"field-sizing  is wild. It's a new CSS property that makes  input ,  textarea  and  select  automatically scale to the size of their content. That means it…","frontmatter":{"title":"Field-sizing just works!","cover":"/blogs/field-sizing/cover.svg","date":"2024-01-30","updated":"2024-04-02"},"fields":{"slug":"/blog/field-sizing-just-works/","date":"2024-01-29T23:00:00.000Z","ogFileName":"field-sizing-just-works"}}},"pageContext":{"slug":"/blog/field-sizing-just-works/"}},"staticQueryHashes":["4164364741","425175329"]}