[go: up one dir, main page]

Skip to content

Design a Layout

This article discusses an approach you can take when designing the layout for your applications.

Textual's layout system is flexible enough to accommodate just about any application design you could conceive of, but it may be hard to know where to start. We will go through a few tips which will help you get over the initial hurdle of designing an application layout.

Tip 1. Make a sketch

The initial design of your application is best done with a sketch. You could use a drawing package such as Excalidraw for your sketch, but pen and paper is equally as good.

Start by drawing a rectangle to represent a blank terminal, then draw a rectangle for each element in your application. Annotate each of the rectangles with the content they will contain, and note wether they will scroll (and in what direction).

For the purposes of this article we are going to design a layout for a Twitter or Mastodon client, which will have a header / footer and a number of columns.

Note

The approach we are discussing here is applicable even if the app you want to build looks nothing like our sketch!

Here's our sketch:

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daW9cdTAwMWK7kv1+f0WQ92VcdTAwMDa46ktcdTAwMTbJXCJ5gcHAi1x1MDAxY1mOl8iKt8GDoaUtyda+eHvIf5+ivKjVUsstW7JbuTaQxGktzWafOudcdTAwMTRZTf7njy9fvvbv2v7Xv7989W9LhXqt3C3cfP3TXHUwMDFkv/a7vVqrSS/B8P+91qBbXHUwMDFhvrPa77d7f//1V6PQvfL77Xqh5HvXtd6gUO/1XHUwMDA35VrLK7VcdTAwMWF/1fp+o/e/7u+9QsP/n3arUe53vdFJUn651m91XHUwMDFmzuXX/Ybf7Pfo2/+P/v/ly3+Gf1x1MDAwN1rX9Uv9QrNS94dcdTAwMWZcdTAwMTi+NGogZ0aFXHUwMDBm77Waw9ZyLoxGxVxyPL+j1tukXHUwMDEz9v0yvXxBjfZHr7hDX1x1MDAwZnL9dT+3q/ay56WbXCK2djHdXHUwMDFkjM57UavXXHUwMDBm+3f1YbtK3Vavl6pcdTAwMTb6peroXHUwMDFkvX63deVcdTAwMWbXyv3qU/dcdTAwMDWOP3+216KuXHUwMDE4farbXHUwMDFhVKpNv+d6gT9cdTAwMWZttVx1MDAwYqVa/254lez56ENX/P1ldOSW/pdC4Vx0LY1VqLW1KtAr7lx1MDAwYkAoXHUwMDBmhFx1MDAxNKhcdTAwMDVarahfQi3baNXpjlDL/sWGP6O2XHUwMDE1XHUwMDBipatcbjWwWX5+T79baPbahS7dt9H7blx1MDAxZa9ZSemhcCdjinM+akfVr1WqfXqHQOZcdTAwMTnOrJQohLs9OGqMP7w1XHUwMDFjjNCopVx1MDAxNs+vuCa0t8tDnPw72HHN8mPHNVx1MDAwN/X6qNXuhXRcdTAwMDBbo89cZtrlwlx1MDAwM1x1MDAwMjhcdTAwMWFcdIpJ1Fxio1x1MDAwZanXmlfhr6u3Slcj0Fxmj/7681x1MDAxNXDVKlx1MDAxYa1WcVx1MDAwNIlcdTAwMThcdTAwMWKsuVLn5367sL63s3+Lza2fXHUwMDA3nZ3tTlx1MDAwNFhDgPtImFx1MDAxYcGVtIpxJjhBIIRT6Vx1MDAxOaUlgcJcdTAwMDJKi8vEqfBcdTAwMThHXHUwMDA1dD6urTaTQFx1MDAwNeMhMiFccjJAQaCeXHUwMDAwKt0zJYGuZvVw6tfrtXZvKkpxXHUwMDA2p1x1MDAxYZCcI1cmNkzbP86OS+lstssvc9f5+3TqrtG8fVxyTPn7wVRxz1xuhdTxQljOQIbolDjOMCMl11x1MDAwZcqgw1x1MDAxMTRcdTAwMTdM/3VRUKBgXHUwMDEyolxceJJcdTAwMDEoi0D/SGPkJEY5eFxuKVKISFx1MDAxOVxiXHUwMDAxUocxyql9ilNcdTAwMWL5b1x1MDAwNVItbFx1MDAxNEi1QXfNXHUwMDEwX/dP1jrpu8ZB7qC4tnXT2upvna3jTcIxXG7KY9TdyLnhlsBcdTAwMDEhiFxuT1x1MDAxObRkXG4kUZhV8o1cdTAwMTAtMqaWXHUwMDA1Uc1cdTAwMTXpgeC/XHUwMDE5Qq2IQijnjJSFS81iQ7TeSbdTV1x1MDAxNexs1upXWLrrb53UXHUwMDBlk1xyUVx1MDAxMljgXHUwMDAyQWjBmWRWh1x1MDAxMFxujmQl3Vx1MDAxNVxyxLVv41BcdTAwMGVFY3BZXHUwMDAwpcsgqkcuV1DoZ+dPKlx1MDAxMqPu1iihXHUwMDAzb3hcdKLr37ZcbrsldbLV2V1vnt3+OL6pXHUwMDFjNJJcctFcdTAwMTTlXCKk4lZcdTAwMTFFcnBJyThGXHUwMDExXHQ+hFxmTTTKJcOlYFRJ5oym4Vx1MDAxNCnaQiBOXHUwMDAyOZMnNaekytphO0CFIUquTJGLNYrNXHUwMDAx0SewjOBcIlx1MDAxZY/8ikbu82dGn1x1MDAwZcCt79+O3HZcdTAwMDBcdTAwMWPd9mG1cbB9tj/YK+VzlbXWgVx1MDAxOdx9fX7fr8ffosJCXGImjTLWLiosxtpcdTAwMTmMXGJcdTAwMTNcdTAwMTVcdTAwMTCczs5dvqBiR8T0i55cdTAwMWVcdTAwMTHVQqk66PpcdTAwMWZcdTAwMWZcdTAwMTNcXCO5W+JKQIK+YTLE21x1MDAxYTxcdTAwMDdB4nNcdTAwMTlcbpnXXHUwMDA0XHUwMDA1xVx1MDAxZbuYYn4p8oBcdTAwMTPaXHUwMDE5p1x1MDAwZSeRmJafTVx1MDAwNoFcdTAwMDWuyZ3PXHUwMDE1XHUwMDA0r+Pp11x1MDAwMHJ041vN/mHt3vU9sLGjW4VGrX43du+GSKWuyviFst/9OnZ8rV6rONR+LVF7gy9cdTAwMTJ2+7VSof78hkatXFxcdTAwMGVqQIlOVqg1/e52XHUwMDFj7m51a5Vas1DPR7SFrt3PPOupXHUwMDE3uDPFQs93r1x1MDAwZVx1MDAwM+j1Qlx1MDAwNVx1MDAxOJmTkns0QltcdTAwMTN/5GRtc6O+M2Bddn/XuK/YfL5/lc68LizfcexEXHUwMDE5j5NRkoxsfUhcdTAwMDPcN1xihp7ipGQ8XHUwMDE4XHUwMDE3XHUwMDBiXHUwMDFlNeGKuEFScvEkiKPrXHUwMDFmhSX5KbCGXHUwMDFisnXKqCA/PLkp0IZy6sDgz1x1MDAxMt2UJt5cdTAwMGVkP0t1U1x1MDAwMapcZouHM/zW5fmxUdqpbbT2sr3NXGZ2N1x1MDAwZTK57av9evpV43vvOXBcIjy6t+hcdTAwMTJwMis6hFBuKCdcdTAwMTX0gmIhYLzGTalCoXgh5TTPzz1GSIUpg89Selx1MDAxMjSzj01cYjbxXHUwMDExnUJcdTAwMWKlmVx0XGZcYr5cdTAwMTWcrzNM2Xbnvt45Ov/x48f5Sf3i6vgwfXxcdTAwMWMwTH9O/9qHXHUwMDBm51t7lUIu5Z9095pH9lx1MDAxMCv1y+1cdTAwMWbjZ3k6f6Hbbd3MYcQ4XHUwMDAwhZRQi4qoSCNcdTAwMTY5vkMmjGhcdTAwMGWtiZ+aTO/MhFx1MDAxYrFcdTAwMTRcdTAwMTGpIOtDySBlXHUwMDA1aMLhJJin6bBloFxirnYh2cm0eJLW46QsmnJBy6ZGVcDEPI2MazJvxr5cdTAwMDPFXHUwMDBiSpjIi1x1MDAwNYj1dUaM49jRXHUwMDE5Rix/4/v9JfmwXHUwMDE3SD/sw0JNiWfD5Fx1MDAxYmyYgPDR5/FcdTAwMDLBXGYjb1x1MDAxMt+GVXnaNrYvdzqp6lx1MDAwNuxfyrPTn0onX+BcZnJcdTAwMDGGh+1cdTAwMTdcdTAwMWGPRIVcdTAwMDJEaGCaiVCbPsUteOu3mtlccr9/36hWz5pq/7qU7drcSXxcdTAwMTFcdTAwMDLFKVx1MDAwMdNq+aNcdTAwMDFcdTAwMDFWXGKrkLbcXHUwMDE4w+dcdTAwMTggm37VK6BCJD+KSWHleHYxxL1hXHUwMDFlJSXyzXnHylx1MDAwYpCYXHUwMDAzjMlcdTAwMTWgXHUwMDE3SPnDXHUwMDA1yLLw0ZEtVJJroUT8uen1k5udY5VLnzXRXFzdf9va6ejNXtJcdTAwMTWISN5cdTAwMDBcdC03XHUwMDFjJ4dcdTAwMDEkMOp24iVOIYtGvNFcdTAwMTT+5jqUXHUwMDFmXFyv5fa+X/LU/u3deW692Pl29X1OXHUwMDFkUjJQXHUwMDE4sixcdTAwMWTSkb5cdTAwMGKYkFxcQrBW5iXYT7/qxOuQ8JRigCBcdTAwMTXnlIKG0iHiYU9qXHUwMDEwlFx1MDAwYjFBXHUwMDE27Y1cdTAwMDbsU4xcdTAwMTIgRi/w80eLkTA8fPQpKlxySM1cdTAwMDH1aIzrpaBs9b5cdTAwMWSet9bWe2epn+1jW7lnJ7yQdC1SXHUwMDFlkTjprlx1MDAxNVO1SLteXHUwMDE3Slx1MDAxYm6QXHTFlydGU8ah5cTAc1JU5yB9WG9uXHUwMDFmVlx1MDAwN/5ajde+dUSnXdqZU3U0LF91Zk26kOxcdTAwMDCoOZKf6Vx1MDAxN71cdTAwMDKig0hcdTAwMTIvNJsqOkYsXHUwMDE04Z+qk1x1MDAwMNV5gYk/WnU4RqpcdTAwMGVcdTAwMDHEWUGr45codPZcdTAwMGWvXHUwMDFi6Hc6WXPon9rDwkb7eDPZsmOYp13dNVk9YVx0fCM6f5hlolBcdTAwMTGCXCKTQmk8Yl9cdTAwMTOSXHUwMDAwpuhPKywzXHUwMDFlc1Vj04ORXGLa01x1MDAxOJ6MfZz+ZHTcXG7BR/q0xFwiXHUwMDA1VFxc2PcpJlx1MDAwM1x1MDAxYpmkcCGlq+Ww8f3Qjl83379l6rtcdTAwMTfqqr2pdLfWbq4nXHUwMDFimK6aXGaBOdplXHUwMDE2UYcqXHUwMDFlldJcdTAwMWWlxXRYg5VcdTAwMTbeVvFooGS5P1x0zH9ENVmnWMzk8T6T7V3ft3WuslNax/R8XHUwMDBlytBcdTAwMWTgi4qLyLxcdTAwMWRlVEhoXHUwMDAzwDmzc1D11ItOuIPiWntcdTAwMTKtq8IgymMqVE2mUHiMoXtcdTAwMDe5TXzjXHUwMDEwcmQxmSTAWyvoNCBcZlx1MDAwNNK2hFx1MDAxNJMtyD/FLybbarX6Sysme4G6w1x1MDAwNirclqVcdTAwMTeTXHUwMDA1XHUwMDA3JENRKSkkiacxvk5cdTAwMWSfyduLTnFXZHbSm5mN3Lp/1j9KejFcdTAwMTnnnPyJtGjMw2zl6GtcdTAwMWVqySxFpXJPOHF8c43nSlaUTdelx6Gajla63vJ57fREXFyKrl/cuD1fWHVccmk2W5gwzTZsMnp2k9hcdTAwMWHcJEL86fxcdTAwMDNd6N521i94tre/0Thkx1x1MDAwN+e902RcdTAwMWI2QrdngFx1MDAxMftCuJZ5XHUwMDE4XHUwMDA2wDyrjEW7iDD47SdT1vKnu9vbP1x1MDAwZnY2+1x1MDAxOdk46lx1MDAxZmyd5W3cirXs9o+LU6wp1chu9ZrXR5XCzdXu4irW3NTg0s1cdTAwMWXMKP90s0RcdTAwMWGZjVx1MDAxZE7TezPpbo9SXHUwMDFjrYiOXHUwMDE5Ob6JXHUwMDA0SFxi4ZGlUlxcPZYwvy2cPlx1MDAwN8timr1lXHUwMDBllr3A+lx1MDAxZj1YJmT0xKlcIoiilvGHsEV3/fJ252fZ7MHZcVVV8VStdZOvcKRvJOVcdTAwMTiWsGFEalI4So0oXHUwMDAzs1x1MDAwYnjA7TdcdTAwMTe4/PptrWvWXHUwMDBlb1tcdTAwMWIoikff7zavXHUwMDBi+3M9wyZcdTAwMTRcYlxc+qhDcGooLEQgmUBleXzUT7/qxFx1MDAwYlx1MDAxMXpcdTAwMTZdMkdJ/6RcdTAwMTBcdTAwMTlcdTAwMTJcIvc4r3vY9+3FmqsqRK/AY3KF6Fx1MDAwNXL+cCGy0c8zUL5H2TaK+KtcdTAwMDGcsfNO6qJvMsfVtZ3Uxam6zlx1MDAwNzLQhCqR8lx1MDAxNFx1MDAwNzR0qUJPKJFcdTAwMDTwXGZl92RcdTAwMWQ1uHj5VKJcdTAwMTlKxLLp0rbMs61Byp5cdTAwMWOcXHUwMDE2JWtcdTAwMTVcdTAwMDbzjX9LYiZcdTAwMTbE/1KUKLpCXHUwMDA2NWHBTVx1MDAwYsVcdTAwMDb99IteXHUwMDAxIZJuXHUwMDEwmaDjVlx1MDAxYVx1MDAxOFx1MDAxN1wiKZTHkFx1MDAwMGdcZqi3V2uuqlx1MDAxMP1WXHUwMDE50Vx1MDAwYtz80UIkZ1RQk1WSXHUwMDA20cZcdTAwMTeiXvNO+er7fjs/yFxmWje8t7X5bTfpQiQ98r9aXHUwMDAyQ+VcbmdHXGb0ULQmSIiY0kYwMz5h9Y8uWjtcdTAwMTPrezc3crN4spMvpG4vq7uVg0pcdTAwMDIlR+jIKVeyIHS/jOXxNWf6Va+A5lxi4chcdTAwMWXc8LWRIatl1EJcdTAwMTG+4qozz9pcdTAwMDDJVZ1cdTAwMTeI+KNVh7xeVFSCMlxiwOJPNG01N3c7W6ksbMP5oLnm64vNb1FcdTAwMDNcdTAwMTJcdNFcdTAwMWNQzFOMXHUwMDFiTVx1MDAxZW+yXGJCXHUwMDAwp9RILWpJnZUuWVx1MDAxYq7Ywd6rZE1gdMlcdTAwMWFcdTAwMThB3oDJ+KVcdTAwMDCbOyeH7eyp3fyW/nFweqK+i3w/asWOxJRcdTAwMDLQZXqKYFx1MDAwMUrgtFx1MDAxMWLOPFSWhJPDm59r/u0qXHUwMDAxdvs3p8Wj01x1MDAxY5rMnbzoraW3blpnXHUwMDBim7WUYFx1MDAwMlx1MDAxNfhLrVx1MDAwNFx1MDAxMJFcdTAwMTUxnJO2U0Ns/JVrjptnxVRd18o1xnJZOEinipuphFx1MDAxM7QxRNBaI2pwSz2HaopcdTAwMDE8gr5WXGa5q/JXn/Mks1KFa57ezd3traV+yPtTuGfFjctKMW4hwPFdeW3ztsVcdTAwMGYzuWo5n8FBrnmbW1xcIYCVbOkpyMxVXHUwMDAztNKcXHUwMDFiXHUwMDFi/2nN6b2Z8Fx1MDAxNITMtSeFVdZcdTAwMTlszoJcdTAwMTHzUFx04HJw7Vx1MDAxYziGXHUwMDE20/hMQVY1XHUwMDA1eYH2PzpcdTAwMDVcdTAwMTFcInLgXHUwMDBiKE92i1xixlx1MDAxZvdK93aE/H7q31xcXHUwMDFmd1x1MDAwYmv3ftpcdTAwMWVVo8aiXHUwMDEzI3HaXHUwMDE1u1lheFjEXHUwMDFlSlx1MDAwMcAjkUelXHUwMDFmpkTDXHL7lLjg/b+6YoNGZuOuVsZu75JlZfv84GLO0TD3Z9lSXHUwMDA0PPpcdTAwMDFcdTAwMDRAVFx1MDAwNIb4XHUwMDBmIEy/6MQrkXElaVx1MDAxYbmRjPAvxlFvSIhIXHUwMDE4mISQ7/vUoVXVoVx1MDAxN7j5w3XIRJaJUrRcbuJnmGNcdJutVvU8ZVtHfnX/JNPM1lxuulHNJl+IXGJtQqCSROdhIZIgPWRMuVx1MDAxZFjQ2OC04KdcdTAwMTJNKtH29fqV8Sv5Sr3Fi6Zlv12y9LzzMu+iRFx1MDAxOF1cdTAwMDAjlVuxiFx1MDAwNYpcdTAwMDVegv30q15cdTAwMDEpcvVWyEBcdTAwMWJcdTAwMTGWXCIp0DPcjXxcdG5ccshFbIjxKUVcdTAwMWYrRS+w80dLkdSRUoSKvJLSc9Sk2SNbNlBLr/XyV6XizV1e/cgmfCVcdTAwMDEw6GlGcWCUskqHXHUwMDAzUitPUpxQsqRcdTAwMDAkLi8jWqlCgFx1MDAxMzi9zJTklrxs71c3dnZz+6U7lUDBXHUwMDEx0etkSPdwXHUwMDFiSX98vZl+0SugN1x1MDAxY6zbeskyJoIrRD/UXHUwMDAx6EXi+1NwXHUwMDEyIDgvkPBHXHUwMDBiXHUwMDBlRG+AqrVGXHUwMDAwruOnPt30UZrt7O83hTpJy8Za5uw+9zPZguOqnIGSXG5BXHUwMDA0NNysYywgXHUwMDA1uMdcdTAwMTJcdTAwMDJjcG9cbsjVL1x1MDAwM1x1MDAxMDqQXGIvtVxmQEfv+oRCgWVyjpVr9lmrv5nL+oY31/X6xe4mXjRcdTAwMTO/qix6buEowp52i5mFXHUwMDE28qNcdTAwMWXwrFvv+nFcdTAwMTW0t42SRVx1MDAwMVO6labdhlNuq1x1MDAwM/c1OFx0T5Bu116SXHUwMDExPVxcx1x1MDAwMyZcdTAwMGJcdTAwMDDeXHUwMDE3o1x1MDAxMoRcXPoq5DNYU7n1XHUwMDAyOJujiv460ylmXHUwMDFh9udm+eLozFx1MDAxN1x1MDAwNzlTgGbSnVxmXHUwMDExJlx1MDAwMVx1MDAxMNFwI1x1MDAxMVhgfYSHrSS14yxOyTORhjViedtGk5eR5ExcdTAwMTRcYlx1MDAwYqFAmbGUXGY1yY1wwLKt+9KLUlx1MDAxNuSV5linpnbrlyO8Ut2/6M9wSv1WO8omjTV3Yjma8VMuYjWa6MdjXCLHxITbbFCjiV8nUDXr+3fn1fIgla1s5Vx1MDAxYq3LuzxL/OxcZoWFR1xcL4XVhlEqMl58hpx0h6SXQKeMU55/dFxc58hcdTAwMTZcdTAwMTbyV2dcXF9cdTAwMWNuX36HUjrTb3/G9Vx1MDAwN8b1QzdPM5Qztlx1MDAxMVVuu2vkMv7I2mxCT2pkg/bc9ijKKKVItsdcdTAwMWQloPBcdTAwMDSS41x1MDAxNNxcIuCbxrpnXHUwMDA2tmWeNW5FVMtcdTAwMTRcdTAwMTnHXHUwMDEx31x1MDAwNi0loqA2MOOe0MNcdEvJiXtcdTAwMTSToOdcdTAwMTmMmFxcXGZcdTAwMTFcdTAwMWWPzFhcZnFG5Zog9tOv2my01y90++u1ZrnWrIw3zH849XZcZl84jOjSwLWSeaApYXLzXHUwMDE0bjhSju6s65hC2/G2Ry6c2lxmlj9MsE1cXLrfLL/cpNn11IEmpZhnXHUwMDE0pztkhit6XHUwMDE4hWKiTZR5o9vcW5I9dONQdqJN9UKvv9FqNGp96vmDVq3ZXHUwMDBm9/CwK9dcXNBX/cJcdTAwMDTh0DVcdTAwMDVfXHUwMDBis0PbfeM4yY9++zKKnuF/nn//959T352KwrX7mUD06Nv+XGL+Ozet2ehHq1xmUr9Solx1MDAxY3/4ZracJZXVXHUwMDE0o1x1MDAwNJQpUIbwL9n4g1x1MDAxY1xi3ONusFx1MDAwMJlcdTAwMTaISyyWt8JDbYZLW4xtXGY4mj/gXHUwMDFlXHUwMDEzlFxmW0roXHRcdTAwMWWT01x0XHUwMDFjKWWiTJnNY15cdTAwMTbOasxcYnzVY78xWW22KVx1MDAxZadcdTAwMTBiNNdXXGJARKLZiO6fOcR40m1cdTAwMDIurGTuwVFjZvPa+HWsXHUwMDE0u0yH1/C1XHRkzckukfM1NnIyUlx0NG5p8/iL1lx1MDAxY1X29zZQ9vzaXHUwMDFl+Nnvt53Dw42bpJNLSqJLd4hBrEIlJIyzXHUwMDBiV+5JXHUwMDFkrVxyMolumONNw3Czn8Ux2uNCXHUwMDE5lNHZ0MScXHK3lFxyKVx1MDAwN4x3SYfesMftn7O+91xyK1x1MDAxMc783iU+2OD2one9/8b0Lf5cdTAwMTRcdTAwMTahaNBo9r7811N29qVX6rbq9f9+35QuRjNcdTAwMTYxpVx1MDAxNemHzIxcdTAwMDc9lFtseq5nzWdcdTAwMDM6oZwljEdGXHUwMDEzrHFcdTAwMWJccihcdTAwMDXjXHUwMDAzOI5HXHUwMDE0XHUwMDEzRltcdTAwMTKS5Y7MkunVJM1cbshccmshg9tCjCa3QHiGh2r+nthcdTAwMGKcnVx1MDAxYptSeG8/5MLY7dm3RD80W1x1MDAxN8ezPNBkcyVKpa1Ukk2meZy6XFxcInPVkbONUFRrZu92PO7OXHUwMDE4Uk5jQIOWXG5IlfREczQhkfwsvW4lXHUwMDEycuVEo1bKg0VcdTAwMDPa/Vx1MDAwNKE8p1x1MDAwMYukM2TRdCY510zMs13BbFx1MDAxZE0onVnjaW5cdGSEXCJcdCZUmsxccvfc7CRcYiWXTGfaeJRmSLe/MFx1MDAxMavk09hMoEfo0I55hzvdXHUwMDA0nkp95LRhnVx1MDAwMVx1MDAwNFx1MDAxN194f05T1Ly5dtpYXHUwMDFhp1x1MDAxMYtI4G6tU1wiXG6plbTTSMRyx2jSXHUwMDBlf8zkMFEsZpu9LO5cdTAwMTjPckKVXHUwMDE0YIxcdTAwMDX6VUzSLHiUhJEyuM50T2/halx1MDAxM1skst3PJKZcdTAwMTdGbjxyXHUwMDEynVTOclxu9vjcNtvLJ5TbXGI4XHUwMDFlKkBKLCWA0eOT6NxQz1x1MDAxM945d0umkl1b3pg8R+5m891CXHUwMDBlklx1MDAwMlLBlEJcdTAwMGbKOSlYJcWhXHUwMDFlrvRcdTAwMTBMdlx1MDAxZshNKtRqrlxc8/d1a47ZyPRcZvdop0ZJXHUwMDE2XHUwMDFjXHUwMDEwfnJrZFx1MDAxOEjFXHTsZlg680rPNvsp33FmQ+ZG+MDtduVOOTko71x1MDAxNlx1MDAxNzFuSS73yFx1MDAwMzVcdHGiUatEbdHAXHUwMDFlvjpcdTAwMDHpObkter3nSNvGnfZqPUel88Fpo8GuQF9tnaX3i93SbvbnbtRcIueJoTaNnuSkXHUwMDE3XHUwMDA0fjfXKMeX70CiNiNcZqM/Llx1MDAwZmVLXHUwMDFjN1NIpt1cdTAwMWHlRuhcZuppS9hMKatcdTAwMTSMzLVcclxc5YruXvNYRvDWcaj4ZVx1MDAwNNVWt3bvxoueRn7ed/xpxumXWl5cdTAwMTCoXHUwMDFiXHUwMDBll1xyKcvA7VpcdTAwMTY73mfDIaHxzqX2iF6F21x1MDAwN5TcitLjhdSopVx1MDAwN4xcdTAwMWOkojcuN+Ct52qDrZ3Ykz7gZKwnnVx1MDAwMNqIkSdtXHUwMDE5XHSHlfOE/6In4ubedWpeKzNbVr6EJuKoP9zjXCJ0f8mowlx1MDAxNN/gJvNcdTAwMWZ2d32li5m5a9uYi2Hu4Vx1MDAxOMVcdTAwMDRnROtcdTAwMDKnjIOBoFx1MDAxYqxcdTAwMWTna5fIMTZ5L1bKxURh2v2kJuBcdTAwMWNlYv54PMHXQrt92Ce4Pd9cdTAwMGXCd638yPWjq/x6XfNv1qfUi19cZn+cXGZccvvTUZI/jIFff/z6f3dfuVAifQ== HeaderTweetTweetTweetTweetFooterTweetTweetTweetTweetTweetTweetTweetTweetFixedFixedColumns (vertical scroll)horizontal scroll

It's rough, but it's all we need.

Try in Textual-web

Tip 2. Work outside in

Like a sculpture with a block of marble, it is best to work from the outside towards the center. If your design has fixed elements (like a header, footer, or sidebar), start with those first.

In our sketch we have a header and footer. Since these are the outermost widgets, we will begin by adding them.

Tip

Textual has builtin Header and Footer widgets which you could use in a real application.

The following example defines an app, a screen, and our header and footer widgets. Since we're starting from scratch and don't have any functionality for our widgets, we are going to use the Placeholder widget to help us visualize our design.

In a real app, we would replace these placeholders with more useful content.

from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Placeholder


class Header(Placeholder):  # (1)!
    pass


class Footer(Placeholder):  # (2)!
    pass


class TweetScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Header(id="Header")  # (3)!
        yield Footer(id="Footer")  # (4)!


class LayoutApp(App):
    def on_mount(self) -> None:
        self.push_screen(TweetScreen())


if __name__ == "__main__":
    app = LayoutApp()
    app.run()
  1. The Header widget extends Placeholder.
  2. The footer widget extends Placeholder.
  3. Creates the header widget (the id will be displayed within the placeholder widget).
  4. Creates the footer widget.

LayoutApp #Header

Tip 3. Apply docks

This app works, but the header and footer don't behave as expected. We want both of these widgets to be fixed to an edge of the screen and limited in height. In Textual this is known as docking which you can apply with the dock rule.

We will dock the header and footer to the top and bottom edges of the screen respectively, by adding a little CSS to the widget classes:

from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Placeholder


class Header(Placeholder):
    DEFAULT_CSS = """
    Header {
        height: 3;
        dock: top;
    }
    """


class Footer(Placeholder):
    DEFAULT_CSS = """
    Footer {
        height: 3;
        dock: bottom;
    }
    """


class TweetScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Header(id="Header")
        yield Footer(id="Footer")


class LayoutApp(App):
    def on_ready(self) -> None:
        self.push_screen(TweetScreen())


if __name__ == "__main__":
    app = LayoutApp()
    app.run()

LayoutApp #Header #Footer

The DEFAULT_CSS class variable is used to set CSS directly in Python code. We could define these in an external CSS file, but writing the CSS inline like this can be convenient if it isn't too complex.

When you dock a widget, it reduces the available area for other widgets. This means that Textual will automatically compensate for the 6 additional lines reserved for the header and footer.

Tip 4. Use FR Units for flexible things

After we've added the header and footer, we want the remaining space to be used for the main interface, which will contain the columns in the sketch. This area is flexible (will change according to the size of the terminal), so how do we ensure that it takes up precisely the space needed?

The simplest way is to use fr units. By setting both the width and height to 1fr, we are telling Textual to divide the space equally amongst the remaining widgets. There is only a single widget, so that widget will fill all of the remaining space.

Let's make that change.

from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import Placeholder


class Header(Placeholder):
    DEFAULT_CSS = """
    Header {
        height: 3;
        dock: top;
    }
    """


class Footer(Placeholder):
    DEFAULT_CSS = """
    Footer {
        height: 3;
        dock: bottom;
    }
    """


class ColumnsContainer(Placeholder):
    DEFAULT_CSS = """
    ColumnsContainer {
        width: 1fr;
        height: 1fr;
        border: solid white;
    }
    """  # (1)!


class TweetScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Header(id="Header")
        yield Footer(id="Footer")
        yield ColumnsContainer(id="Columns")


class LayoutApp(App):
    def on_ready(self) -> None:
        self.push_screen(TweetScreen())


if __name__ == "__main__":
    app = LayoutApp()
    app.run()
  1. Here's where we set the width and height to 1fr. We also add a border just to illustrate the dimensions better.

LayoutApp #Header ────────────────────────────────────────────────────────────────────────────── #Columns ────────────────────────────────────────────────────────────────────────────── #Footer

As you can see, the central Columns area will resize with the terminal window.

Tip 5. Use containers

Before we add content to the Columns area, we have an opportunity to simplify. Rather than extend Placeholder for our ColumnsContainer widget, we can use one of the builtin containers. A container is simply a widget designed to contain other widgets. Containers are styled with fr units to fill the remaining space so we won't need to add any more CSS.

Let's replace the ColumnsContainer class in the previous example with a HorizontalScroll container, which also adds an automatic horizontal scrollbar.

from textual.app import App, ComposeResult
from textual.containers import HorizontalScroll
from textual.screen import Screen
from textual.widgets import Placeholder


class Header(Placeholder):
    DEFAULT_CSS = """
    Header {
        height: 3;
        dock: top;
    }
    """


class Footer(Placeholder):
    DEFAULT_CSS = """
    Footer {
        height: 3;
        dock: bottom;
    }
    """


class TweetScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Header(id="Header")
        yield Footer(id="Footer")
        yield HorizontalScroll()  # (1)!


class LayoutApp(App):
    def on_ready(self) -> None:
        self.push_screen(TweetScreen())


if __name__ == "__main__":
    app = LayoutApp()
    app.run()
  1. The builtin container widget.

LayoutApp #Header #Footer

The container will appear as blank space until we add some widgets to it.

Let's add the columns to the HorizontalScroll. A column is itself a container which will have a vertical scrollbar, so we will define our Column by subclassing VerticalScroll. In a real app, these columns will likely be added dynamically from some kind of configuration, but let's add 4 to visualize the layout.

We will also define a Tweet placeholder and add a few to each column.

from textual.app import App, ComposeResult
from textual.containers import HorizontalScroll, VerticalScroll
from textual.screen import Screen
from textual.widgets import Placeholder


class Header(Placeholder):
    DEFAULT_CSS = """
    Header {
        height: 3;
        dock: top;
    }
    """


class Footer(Placeholder):
    DEFAULT_CSS = """
    Footer {
        height: 3;
        dock: bottom;
    }
    """


class Tweet(Placeholder):
    pass


class Column(VerticalScroll):
    def compose(self) -> ComposeResult:
        for tweet_no in range(1, 20):
            yield Tweet(id=f"Tweet{tweet_no}")


class TweetScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Header(id="Header")
        yield Footer(id="Footer")
        with HorizontalScroll():
            yield Column()
            yield Column()
            yield Column()
            yield Column()


class LayoutApp(App):
    def on_ready(self) -> None:
        self.push_screen(TweetScreen())


if __name__ == "__main__":
    app = LayoutApp()
    app.run()

LayoutApp #Header #Tweet1#Tweet1#Tweet1#Tweet1 #Footer

Note from the output that each Column takes a quarter of the screen width. This happens because Column extends a container which has a width of 1fr.

It makes more sense for a column in a Twitter / Mastodon client to use a fixed width. Let's set the width of the columns to 32.

We also want to reduce the height of each "tweet". In the real app, you might set the height to "auto" so it fits the content, but lets set it to 5 lines for now.

Here's the final example and a reminder of the sketch.

from textual.app import App, ComposeResult
from textual.containers import HorizontalScroll, VerticalScroll
from textual.screen import Screen
from textual.widgets import Placeholder


class Header(Placeholder):
    DEFAULT_CSS = """
    Header {
        height: 3;
        dock: top;
    }
    """


class Footer(Placeholder):
    DEFAULT_CSS = """
    Footer {
        height: 3;
        dock: bottom;
    }
    """


class Tweet(Placeholder):
    DEFAULT_CSS = """
    Tweet {
        height: 5;
        width: 1fr;
        border: tall $background;
    }
    """


class Column(VerticalScroll):
    DEFAULT_CSS = """
    Column {
        height: 1fr;
        width: 32;
        margin: 0 2;
    }
    """

    def compose(self) -> ComposeResult:
        for tweet_no in range(1, 20):
            yield Tweet(id=f"Tweet{tweet_no}")


class TweetScreen(Screen):
    def compose(self) -> ComposeResult:
        yield Header(id="Header")
        yield Footer(id="Footer")
        with HorizontalScroll():
            yield Column()
            yield Column()
            yield Column()
            yield Column()


class LayoutApp(App):
    def on_ready(self) -> None:
        self.push_screen(TweetScreen())


if __name__ == "__main__":
    app = LayoutApp()
    app.run()

LayoutApp #Header ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ #Tweet1#Tweet1#Tweet1 ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ ▃▃▃▃ #Tweet2#Tweet2#Tweet2 ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ #Tweet3#Tweet3#Tweet3 ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ #Tweet4#Tweet4#Tweet4 ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ #Tweet5#Tweet5#Tweet5 ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ #Footer

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daW9cdTAwMWK7kv1+f0WQ92VcdTAwMDa46ktcdTAwMTbJXCJ5gcHAi1x1MDAxY1mOl8iKt8GDoaUtyda+eHvIf5+ivKjVUsstW7JbuTaQxGktzWafOudcdTAwMTRZTf7njy9fvvbv2v7Xv7989W9LhXqt3C3cfP3TXHUwMDFkv/a7vVqrSS/B8P+91qBbXHUwMDFhvrPa77d7f//1V6PQvfL77Xqh5HvXtd6gUO/1XHUwMDA35VrLK7VcdTAwMWF/1fp+o/e/7u+9QsP/n3arUe53vdFJUn651m91XHUwMDFmzuXX/Ybf7Pfo2/+P/v/ly3+Gf1x1MDAwN1rX9Uv9QrNS94dcdTAwMWZcdTAwMTi+NGogZ0aFXHUwMDBm77Waw9ZyLoxGxVxyPL+j1tukXHUwMDEz9v0yvXxBjfZHr7hDX1x1MDAwZnL9dT+3q/ay56WbXCK2djHdXHUwMDFkjM57UavXXHUwMDBm+3f1YbtK3Vavl6pcdTAwMTb6peroXHUwMDFkvX63deVcdTAwMWbXyv3qU/dcdTAwMDWOP3+216KuXHUwMDE4farbXHUwMDFhVKpNv+d6gT9cdTAwMWZttVx1MDAwYqVa/254lez56ENX/P1ldOSW/pdC4Vx0LY1VqLW1KtAr7lx1MDAwYkAoXHUwMDBmhFx1MDAxNKhcdTAwMDVarahfQi3baNXpjlDL/sWGP6O2XHUwMDE1XHUwMDBipatcbjWwWX5+T79baPbahS7dt9H7blx1MDAxZa9ZSemhcCdjinM+akfVr1WqfXqHQOZcdTAwMTnOrJQohLs9OGqMP7w1XHUwMDFjjNCopVx1MDAxNs+vuCa0t8tDnPw72HHN8mPHNVx1MDAwN/X6qNXuhXRcdTAwMDBbo89cZtrlwlx1MDAwM1x1MDAwMjhcdTAwMWFcdIpJ1Fxio1x1MDAwZanXmlfhr6u3Slcj0Fxmj/7681x1MDAxNXDVKlx1MDAxYa1WcVx1MDAwNIlcdTAwMThcdTAwMWKsuVLn5367sL63s3+Lza2fXHUwMDA3nZ3tTlx1MDAwNFhDgPtImFx1MDAxYcGVtIpxJjhBIIRT6Vx1MDAxOaUlgcJcdTAwMDJKi8vEqfBcdTAwMThHXHUwMDA1dD6urTaTQFx1MDAwNeMhMiFccjJAQaCeXHUwMDAwKt0zJYGuZvVw6tfrtXZvKkpxXHUwMDA2p1x1MDAxYZCcI1cmNkzbP86OS+lstssvc9f5+3TqrtG8fVxyTPn7wVRxz1xuhdTxQljOQIbolDjOMCMl11x1MDAwZcqgw1x1MDAxMTRcdTAwMTdM/3VRUKBgXHUwMDEyolxceJJcdTAwMDEoi0D/SGPkJEY5eFxuKVKISFx1MDAxOVxiXHUwMDAxUocxyql9ilNcdTAwMWL5b1x1MDAwNVItbFx1MDAxNEi1QXfNXHUwMDEwX/dP1jrpu8ZB7qC4tnXT2upvna3jTcIxXG7KY9TdyLnhlsBcdTAwMDEhiFxuT1x1MDAxObRkXG4kUZhV8o1cdTAwMTAtMqaWXHUwMDA1Uc1cdTAwMTXpgeC/XHUwMDE5Qq2IQijnjJSFS81iQ7TeSbdTV1x1MDAxNexs1upXWLrrb53UXHUwMDBlk1xyUVx1MDAxMljgXHUwMDAyQWjBmWRWh1x1MDAxMFxujmQl3Vx1MDAxNVxyxLVv41BcdTAwMGVFY3BZXHUwMDAwpcsgqkcuV1DoZ+dPKlx1MDAxMqPu1iihXHUwMDAzb3hcdKLr37ZcbrsldbLV2V1vnt3+OL6pXHUwMDFjNJJcctFcdTAwMTTlXCKk4lZcdTAwMTFFcnBJyThGXHUwMDExXHQ+hFxmTTTKJcOlYFRJ5oym4Vx1MDAxNCnaQiBOXHUwMDAyOZMnNaekytphO0CFIUquTJGLNYrNXHUwMDAx0SewjOBcIlx1MDAxZY/8ikbu82dGn1x1MDAwZcCt79+O3HZcdTAwMDBcdTAwMWPd9mG1cbB9tj/YK+VzlbXWgVx1MDAxOdx9fX7fr8ffosJCXGImjTLWLiosxtpcdTAwMTmMXGJcdTAwMTNcdTAwMTVcdTAwMTCczs5dvqBiR8T0i55cdTAwMWVcdTAwMTHVQqk66PpcdTAwMWZcdTAwMWZcdTAwMTNcXCO5W+JKQIK+YTLE21x1MDAxYTxcdTAwMDdB4nNcdTAwMTlcbpnXXHUwMDA0XHUwMDA1xVx1MDAxZbuYYn4p8oBcdTAwMTPaXHUwMDE5p1x1MDAwZSeRmJafTVx1MDAwNoFcdTAwMDWuyZ3PXHUwMDE1XHUwMDA0r+Pp11x1MDAwMHJ041vN/mHt3vU9sLGjW4VGrX43du+GSKWuyviFst/9OnZ8rV6rONR+LVF7gy9cdTAwMTJ2+7VSof78hkatXFxcdTAwMGVqQIlOVqg1/e52XHUwMDFj7m51a5Vas1DPR7SFrt3PPOupXHUwMDE3uDPFQs93r1x1MDAwZVx1MDAwM+j1Qlx1MDAwNVx1MDAxOJmTkns0QltcdTAwMTN/5GRtc6O+M2Bddn/XuK/YfL5/lc68LizfcexEXHUwMDE5j5NRkoxsfUhcdTAwMDPcN1xihp7ipGQ8XHUwMDE4XHUwMDE3XHUwMDBiXHUwMDFlNeGKuEFScvEkiKPrXHUwMDFmhSX5KbCGXHUwMDFisnXKqCA/PLkp0IZy6sDgz1x1MDAxMt2UJt5cdTAwMGVkP0t1U1x1MDAwMapcZouHM/zW5fmxUdqpbbT2sr3NXGZ2N1x1MDAwZTK57av9evpV43vvOXBcIjy6t+hcdTAwMTJwMis6hFBuKCdcdTAwMTX0gmIhYLzGTalCoXgh5TTPzz1GSIUpg89Selx1MDAxMjSzj01cYjbxXHUwMDExnUJcdTAwMWKlmVx0XGZcYr5cdTAwMTWcrzNM2Xbnvt45Ov/x48f5Sf3i6vgwfXxcdTAwMWMwTH9O/9qHXHUwMDBm51t7lUIu5Z9095pH9lx1MDAxMCv1y+1cdTAwMWbjZ3k6f6Hbbd3MYcQ4XHUwMDAwhZRQi4qoSCNcdTAwMTY5vkMmjGhcdTAwMGWtiZ+aTO/MhFx1MDAxYrFcdTAwMTRcdTAwMTGpIOtDySBlXHUwMDA1aMLhJJin6bBloFxirnYh2cm0eJLW46QsmnJBy6ZGVcDEPI2MazJvxr5cdTAwMDPFXHUwMDBiSpjIi1x1MDAwNYj1dUaM49jRXHUwMDE5Rix/4/v9JfmwXHUwMDE3SD/sw0JNiWfD5Fx1MDAxYmyYgPDR5/FcdTAwMDLBXGYjb1x1MDAxMt+GVXnaNrYvdzqp6lx1MDAwNuxfyrPTn0onX+BcZnJcdTAwMDGGh+1cdTAwMTdcdTAwMWGPRIVcdTAwMDJEaGCaiVCbPsUteOu3mtlccr9/36hWz5pq/7qU7drcSXxcdTAwMTFcdTAwMDLFKVx1MDAwMdNq+aNcdTAwMDFcdTAwMDFWXGKrkLbcXHUwMDE4w+dcdTAwMTggm37VK6BCJD+KSWHleHYxxL1hXHUwMDFlJSXyzXnHylx1MDAwYpCYXHUwMDAzjMlcdTAwMTWgXHUwMDE3SPnDXHUwMDA1yLLw0ZEtVJJroUT8uen1k5udY5VLnzXRXFzdf9va6ejNXtJcdTAwMTWISN5cdTAwMDBcdC03XHUwMDFjJ4dcdTAwMDEkMOp24iVOIYtGvNFcdTAwMTT+5jqUXHUwMDFmXFyv5fa+X/LU/u3deW692Pl29X1OXHUwMDFkUjJQXHUwMDE4sixcdTAwMWTSkb5cdTAwMGKYkFxcQrBW5iXYT7/qxOuQ8JRigCBcdTAwMTXnlIKG0iHiYU9qXHUwMDEwlFx1MDAwYjFBXHUwMDE27Y1cdTAwMDbsU4xcdTAwMTIgRi/w80eLkTA8fPQpKlxySM1cdTAwMDH1aIzrpaBs9b5cdTAwMWSet9bWe2epn+1jW7lnJ7yQdC1SXHUwMDFlkTjprlx1MDAxNVO1SLteXHUwMDE3Slx1MDAxYm6QXHTFlydGU8ah5cTAc1JU5yB9WG9uXHUwMDFmVlx1MDAwN/5ajde+dUSnXdqZU3U0LF91Zk26kOxcdTAwMDCoOZKf6Vx1MDAxN71cdTAwMDKig0hcdTAwMTIvNJsqOkYsXHUwMDE04Z+qk1x1MDAwMNV5gYk/WnU4RqpcdTAwMGVcdTAwMDHEWUGr45codPZcdTAwMGWvXHUwMDFi6Hc6WXPon9rDwkb7eDPZsmOYp13dNVk9YVx0fCM6f5hlolBcdTAwMTGCXCKTQmk8Yl9cdTAwMTOSXHUwMDAwpuhPKywzXHUwMDFlc1Vj04ORXGLa01x1MDAxOJ6MfZz+ZHTcXG7BR/q0xFwiXHUwMDA1VFxc2PcpJlx1MDAwM1x1MDAxYpmkcCGlq+Ww8f3Qjl83379l6rtcdTAwMTfqqr2pdLfWbq4nXHUwMDFimK6aXGaBOdplXHUwMDE2UYcqXHUwMDFlldJcdTAwMWWlxXRYg5VcdTAwMTbeVvFooGS5P1x0zH9ENVmnWMzk8T6T7V3ft3WuslNax/R8XHUwMDBlytBcdTAwMWTgi4qLyLxcdTAwMWRlVEhoXHUwMDAzwDmzc1D11ItOuIPiWntcdTAwMTKtq8IgymMqVE2mUHiMoXtcdTAwMDe5TXzjXHUwMDEwcmQxmSTAWyvoNCBcZlx1MDAwNNK2hFx1MDAxNJMtyD/FLybbarX6Sysme4G6w1x1MDAwNirclqVcdTAwMTeTXHUwMDA1XHUwMDA3JENRKSkkiacxvk5cdTAwMWSfyduLTnFXZHbSm5mN3Lp/1j9KejFcdTAwMTnnnPyJtGjMw2zl6GtcdTAwMWVqySxFpXJPOHF8c43nSlaUTdelx6Gajla63vJ57fREXFyKrl/cuD1fWHVccmk2W5gwzTZsMnp2k9hcdTAwMWHcJEL86fxcdTAwMDNd6N521i94tre/0Thkx1x1MDAwN+e902RcdTAwMWI2QrdngFx1MDAxMftCuJZ5XHUwMDE4XHUwMDA2wDyrjEW7iDD47SdT1vKnu9vbP1x1MDAwZnY2+1x1MDAxOdk46lx1MDAxZmyd5W3cirXs9o+LU6wp1chu9ZrXR5XCzdXu4irW3NTg0s1cdTAwMWXMKP90s0RcdTAwMWGZjVx1MDAxZE7TezPpbo9SXHUwMDFjrYiOXHUwMDE5Ob6JXHUwMDA0SFxi4ZGlUlxcPZYwvy2cPlx1MDAwN8timr1lXHUwMDBllr3A+lx1MDAxZj1YJmT0xKlcIoiilvGHsEV3/fJ252fZ7MHZcVVV8VStdZOvcKRvJOVcdTAwMTiWsGFEalI4So0oXHUwMDAzs1x1MDAwYnjA7TdcdTAwMTe4/PptrWvWXHUwMDBlb1tcdTAwMWIoikff7zavXHUwMDBi+3M9wyZcdTAwMTRcYlxc+qhDcGooLEQgmUBleXzUT7/qxFx1MDAwYlx1MDAxMXpcdTAwMTZdMkdJ/6RcdTAwMTBcdTAwMTlcdTAwMTJcIvc4r3vY9+3FmqsqRK/AY3KF6Fx1MDAwNXL+cCGy0c8zUL5H2TaK+KtcdTAwMDGcsfNO6qJvMsfVtZ3Uxam6zlx1MDAwNzLQhCqR8lx1MDAxNFx1MDAwNzR0qUJPKJFcdTAwMDTwXGZl92RcdTAwMWQ1uHj5VKJcdTAwMTlKxLLp0rbMs61Byp5cdTAwMWOcXHUwMDE2JWtcdTAwMTVcdTAwMDbzjX9LYiZcdTAwMTbE/1KUKLpCXHUwMDA2NWHBTVx1MDAwYsVcdTAwMDb99IteXHUwMDAxIZJuXHUwMDEwmaDjVlx1MDAxYVx1MDAxOFx1MDAxN1wiKZTHkFx1MDAwMGdcZqi3V2uuqlx1MDAxMP1WXHUwMDE50Vx1MDAwYtz80UIkZ1RQk1WSXHUwMDA20cZcdTAwMTeiXvNO+er7fjs/yFxmWje8t7X5bTfpQiQ98r9aXHUwMDAyQ+VcbmdHXGb0ULQmSIiY0kYwMz5h9Y8uWjtcdTAwMTPrezc3crN4spMvpG4vq7uVg0pcdTAwMDIlR+jIKVeyIHS/jOXxNWf6Va+A5lxi4chcdTAwMWXc8LWRIatl1EJcdTAwMTG+4qozz9pcdTAwMDDJVZ1cdTAwMTeI+KNVh7xeVFSCMlxiwOJPNG01N3c7W6ksbMP5oLnm64vNb1FcdTAwMDNcdTAwMTJcdNFcdTAwMWNQzFOMXHUwMDFiTVx1MDAxZW+yXGJCXHUwMDAwp9RILWpJnZUuWVx1MDAxYq7Ywd6rZE1gdMlcdTAwMWFcdTAwMThB3oDJ+KVcdTAwMDCbOyeH7eyp3fyW/nFweqK+i3w/asWOxJRcdTAwMDLQZXqKYFx1MDAwMUrgtFx1MDAxMWLOPFSWhJPDm59r/u0qXHUwMDAxdvs3p8Wj01x1MDAxY5rMnbzoraW3blpnXHUwMDBim7WUYFx1MDAwMlx1MDAxNfhLrVx1MDAwNFx1MDAxMJFcdTAwMTUxnJO2U0Ns/JVrjptnxVRd18o1xnJZOEinipuphFx1MDAxM7QxRNBaI2pwSz2HaopcdTAwMDE8gr5WXGa5q/JXn/Mks1KFa57ezd3traV+yPtTuGfFjctKMW4hwPFdeW3ztsVcdTAwMGYzuWo5n8FBrnmbW1xcIYCVbOkpyMxVXHUwMDAztNKcXHUwMDFiXHUwMDFi/2nN6b2Z8Fx1MDAxNITMtSeFVdZcdTAwMTlszoJcdTAwMTHzUFx04HJw7Vx1MDAxYziGXHUwMDE20/hMQVY1XHUwMDA1eYH2PzpcdTAwMDVcdTAwMTFcInLgXHUwMDBiKE92i1xixlx1MDAxZvdK93aE/H7q31xcXHUwMDFmd1x1MDAwYmv3ftpcdTAwMWVVo8aiXHUwMDEzI3HaXHUwMDE1u1lheFjEXHUwMDFlSlx1MDAwMcAjkUelXHUwMDFmpkTDXHL7lLjg/b+6YoNGZuOuVsZu75JlZfv84GLO0TD3Z9lSXHUwMDA0PPpcdTAwMDFcdTAwMDRAVFx1MDAwNIb4XHUwMDBmIEy/6MQrkXElaVx1MDAxYbmRjPAvxlFvSIhIXHUwMDE4mISQ7/vUoVXVoVx1MDAxN7j5w3XIRJaJUrRcbuJnmGNcdJutVvU8ZVtHfnX/JNPM1lxuulHNJl+IXGJtQqCSROdhIZIgPWRMuVx1MDAxZFjQ2OC04KdcdTAwMTJNKtH29fqV8Sv5Sr3Fi6Zlv12y9LzzMu+iRFx1MDAxOF1cdTAwMDAjlVuxiFx1MDAwNYpcdTAwMDVegv30q15cdTAwMDEpcvVWyEBcdTAwMWJcdTAwMTGWXCIp0DPcjXxcdG5ccshFbIjxKUVcdTAwMWYrRS+w80dLkdSRUoSKvJLSc9Sk2SNbNlBLr/XyV6XizV1e/cgmfCVcdTAwMDEw6GlGcWCUskqHXHUwMDAzUitPUpxQsqRcdTAwMDAkLi8jWqlCgFx1MDAxMzi9zJTklrxs71c3dnZz+6U7lUDBXHUwMDEx0etkSPdwXHUwMDFiSX98vZl+0SugN1x1MDAxY6zbeskyJoIrRD/UXHUwMDAx6EXi+1NwXHUwMDEyIDgvkPBHXHUwMDBiXHUwMDBlRG+AqrVGXHUwMDAwruOnPt30UZrt7O83hTpJy8Za5uw+9zPZguOqnIGSXG5BXHUwMDA0NNysYywgXHUwMDA1uMdcdTAwMTJcdTAwMDJjcG9cbsjVL1x1MDAwM1x1MDAxMDqQXGIvtVxmQEfv+oRCgWVyjpVr9lmrv5nL+oY31/X6xe4mXjRcdTAwMTO/qix6buEowp52i5mFXHUwMDE28qNcdTAwMWXwrFvv+nFcdTAwMTW0t42SRVx1MDAwMVO6labdhlNuq1x1MDAwM/c1OFx0T5Bu116SXHUwMDExPVxcx1x1MDAwMyZcdTAwMGJcdTAwMDDeXHUwMDE3o1x1MDAxMoRcXPoq5DNYU7n1XHUwMDAyOJujiv460ylmXHUwMDFh9udm+eLozFx1MDAxN1x1MDAwNzlTgGbSnVxmXHUwMDExJlx1MDAwMVx1MDAxMNFwI1x1MDAxMVhgfYSHrSS14yxOyTORhjViedtGk5eR5ExcdTAwMTRcYlx1MDAwYqFAmbGUXGY1yY1wwLKt+9KLUlx1MDAxNuSV5linpnbrlyO8Ut2/6M9wSv1WO8omjTV3Yjma8VMuYjWa6MdjXCLHxITbbFCjiV8nUDXr+3fn1fIgla1s5Vx1MDAxYq3LuzxL/OxcZoWFR1xcL4XVhlEqMl58hpx0h6SXQKeMU55/dFxc58hcdTAwMTZcdTAwMTbyV2dcXF9cdTAwMWNuX36HUjrTb3/G9Vx1MDAwN8b1QzdPM5Qztlx1MDAxMVVuu2vkMv7I2mxCT2pkg/bc9ijKKKVItsdcdTAwMWQloPBcdTAwMDSS41x1MDAxNNxcIuCbxrpnXHUwMDA2tmWeNW5FVMtcdTAwMTRcdTAwMTnHXHUwMDEx31x1MDAwNi0loqA2MOOe0MNcdEvJiXtcdTAwMTSToOdcdTAwMTmMmFxcXGZcdTAwMTFcdTAwMWWPzFhcZnFG5Zog9tOv2my01y90++u1ZrnWrIw3zH849XZcZl84jOjSwLWSeaApYXLzXHUwMDE0bjhSju6s65hC2/G2Ry6c2lxmlj9MsE1cXLrfLL/cpNn11IEmpZhnXHUwMDE0pztkhit6XHUwMDE4hWKiTZR5o9vcW5I9dONQdqJN9UKvv9FqNGp96vmDVq3ZXHUwMDBm9/CwK9dcXNBX/cJcdTAwMDTh0DVcdTAwMDVfXHUwMDBis0PbfeM4yY9++zKKnuF/nn//959T352KwrX7mUD06Nv+XGL+Ozet2ehHq1xmUr9Solx1MDAxY3/4ZracJZXVXHUwMDE0o1x1MDAwNJQpUIbwL9n4g1x1MDAxY1xi3ONusFx1MDAwMJlcdTAwMTaISyyWt8JDbYZLW4xtXGY4mj/gXHUwMDFlXHUwMDEzlFxmW0roXHRcdTAwMWWT01x0XHUwMDFjKWWiTJnNY15cdTAwMTbOasxcYnzVY78xWW22KVx1MDAxZadcdTAwMTBiNNdXXGJARKLZiO6fOcR40m1cdTAwMDIurGTuwVFjZvPa+HWsXHUwMDE0u0yH1/C1XHRkzckukfM1NnIyUlx0NG5p8/iL1lx1MDAxY1X29zZQ9vzaXHUwMDFl+Nnvt53Dw42bpJNLSqJLd4hBrEIlJIyzXHUwMDBiV+5JXHUwMDFkrVxyMolumONNw3Czn8Ux2uNCXHUwMDE5lNHZ0MScXHK3lFxyKVx1MDAwN4x3SYfesMftn7O+91xyK1x1MDAxMc783iU+2OD2one9/8b0Lf5cdTAwMTRcdTAwMTahaNBo9r7811N29qVX6rbq9f9+35QuRjNcdTAwMTYxpVx1MDAxNemHzIxcdTAwMDc9lFtseq5nzWdcdTAwMDM6oZwljEdGXHUwMDEzrHFcdTAwMWJccihcdTAwMDXjXHUwMDAzOI5HXHUwMDE0XHUwMDEzRltcdTAwMTKS5Y7MkunVJM1cbshccmshg9tCjCa3QHiGh2r+nthcdTAwMGKcnVx1MDAxYptSeG8/5MLY7dm3RD80W1x1MDAxN8ezPNBkcyVKpa1Ukk2meZy6XFxcInPVkbONUFRrZu92PO7OXHUwMDE4Uk5jQIOWXG5IlfREczQhkfwsvW4lXHUwMDEycuVEo1bKg0VcdTAwMDPa/Vx1MDAwNKE8p1x1MDAwMYukM2TRdCY510zMs13BbFx1MDAxZE0onVnjaW5cdGSEXCJcdCZUmsxccvfc7CRcYiWXTGfaeJRmSLe/MFx1MDAxMavk09hMoEfo0I55hzvdXHUwMDA0nkp95LRhnVx1MDAwMVx1MDAwNFx1MDAxN194f05T1Ly5dtpYXHUwMDFhp1x1MDAxMYtI4G6tU1wiXG6plbTTSMRyx2jSXHUwMDBlf8zkMFEsZpu9LO5cdTAwMTjPckKVXHUwMDE0YIxcdTAwMDX6VUzSLHiUhJEyuM50T2/halx1MDAxM1skst3PJKZcdTAwMTdGbjxyXHUwMDEynVTOclxu9vjcNtvLJ5TbXGI4XHUwMDFlKkBKLCWA0eOT6NxQz1x1MDAxM945d0umkl1b3pg8R+5m891CXHUwMDBlklx1MDAwMlLBlEJcdTAwMGbKOSlYJcWhXHUwMDFlrvRcdTAwMTBMdlx1MDAxZshNKtRqrlxc8/d1a47ZyPRcZvdop0ZJXHUwMDE2XHUwMDFjXHUwMDEwfnJrZFx1MDAxOEjFXHTsZlg680rPNvsp33FmQ+ZG+MDtduVOOTko71x1MDAxNlx1MDAxNzFuSS73yFx1MDAwMzVcdHGiUatEbdHAXHUwMDFlvjpcdTAwMDHpObkter3nSNvGnfZqPUel88Fpo8GuQF9tnaX3i93SbvbnbtRcIueJoTaNnuSkXHUwMDE3XHUwMDA0fjfXKMeX70CiNiNcZqM/Llx1MDAwZmVLXHUwMDFjN1NIpt1cdTAwMWHlRuhcZuppS9hMKatcdTAwMTSMzLVcclxc5YruXvNYRvDWcaj4ZVx1MDAwNNVWt3bvxoueRn7ed/xpxumXWl5cdTAwMTCoXHUwMDFiXHUwMDBll1xyKcvA7VpcdTAwMTY73mfDIaHxzqX2iF6F21x1MDAwN5TcitLjhdSopVx1MDAwN4xcdTAwMWOkojcuN+Ct52qDrZ3Ykz7gZKwnnVx1MDAwMNqIkSdtXHUwMDE5XHSHlfOE/6In4ubedWpeKzNbVr6EJuKoP9zjXCJ0f8mowlx1MDAxNN/gJvNcdTAwMWZ2d32li5m5a9uYi2Hu4Vx1MDAxOMVcdTAwMDRnROtcdTAwMDKnjIOBoFx1MDAxYqxcdTAwMWTna5fIMTZ5L1bKxURh2v2kJuBcdTAwMWNlYv54PMHXQrt92Ce4Pd9cdTAwMGXCd638yPWjq/x6XfNv1qfUi19cZn+cXGZccvvTUZI/jIFff/z6f3dfuVAifQ== HeaderTweetTweetTweetTweetFooterTweetTweetTweetTweetTweetTweetTweetTweetFixedFixedColumns (vertical scroll)horizontal scroll

A layout like this is a great starting point. In a real app, you would start replacing each of the placeholders with builtin or custom widgets.

Summary

Layout is the first thing you will tackle when building a Textual app. The following tips will help you get started.

  1. Make a sketch (pen and paper is fine).
  2. Work outside in. Start with the entire space of the terminal, add the outermost content first.
  3. Dock fixed widgets. If the content doesn't move or scroll, you probably want to dock it.
  4. Make use of fr for flexible space within layouts.
  5. Use containers to contain other widgets, particularly if they scroll!

If you need further help, we are here to help.