{"id":6373,"date":"2020-01-01T11:04:12","date_gmt":"2020-01-01T10:04:12","guid":{"rendered":"https:\/\/monodes.com\/predaelli\/?p=6373"},"modified":"2020-01-01T11:04:12","modified_gmt":"2020-01-01T10:04:12","slug":"how-to-style-your-gtk-app-with-css-and-haskell-codeburst","status":"publish","type":"post","link":"https:\/\/monodes.com\/predaelli\/2020\/01\/01\/how-to-style-your-gtk-app-with-css-and-haskell-codeburst\/","title":{"rendered":"How to Style Your GTK App with CSS and Haskell &#8211; codeburst"},"content":{"rendered":"<blockquote><p>For the upcoming release of Gifcurry \u2014 an open-source, video-to-GIF maker \u2014 a completely custom theme was made using CSS and Haskell. The\u2026<\/p><\/blockquote>\n<p>Source: <em><a href=\"https:\/\/codeburst.io\/how-to-style-your-gtk-app-with-css-and-haskell-1aa99c87eb8f\">How to Style Your GTK App with CSS and Haskell &#8211; codeburst<\/a><\/em><\/p>\n<p><!--more--><!--nextpage--><\/p>\n<blockquote>\n<div id=\"32ee\" class=\"fh fi cn ar fj b fk fl fm fn fo fp fq fr fs ft fu\">\n<h1 class=\"fj b fk fv fm fw fo fx fq fy fs fz cn\">How to Style Your GTK App with CSS and Haskell<\/h1>\n<\/div>\n<div class=\"ga\">\n<div class=\"n gb gc gd ge\">\n<div class=\"o n\">\n<div><a href=\"https:\/\/codeburst.io\/@lettier?source=post_page-----1aa99c87eb8f----------------------\" rel=\"noopener\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"r dg gf gg\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/fit\/c\/48\/48\/1%2ADM2bfIVj3LLgyG3hMirauw.png?resize=48%2C48&#038;ssl=1\" alt=\"Lettier\" width=\"48\" height=\"48\" \/><\/a><\/div>\n<div class=\"gh ak r\">\n<div class=\"n\">\n<div>\n<div class=\"gi n o gj\"><span class=\"aq ed bj as bd gk bc gl gm gn cn\"><a class=\"bo bp bq br bs bt bu bv bw bx go ca cb cc cd\" href=\"https:\/\/codeburst.io\/@lettier?source=post_page-----1aa99c87eb8f----------------------\" rel=\"noopener\">Lettier<\/a><\/span><\/p>\n<div class=\"gp r ap h\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"o n\">\n<div class=\"gh ak r\">\n<div><a class=\"bo bp bq br bs bt bu bv bw bx go ca cb cc cd\" href=\"https:\/\/codeburst.io\/how-to-style-your-gtk-app-with-css-and-haskell-1aa99c87eb8f?source=post_page-----1aa99c87eb8f----------------------\" rel=\"noopener\">Jun 28, 2018<\/a> \u00b7 5 min read<\/div>\n<\/div>\n<\/div>\n<div class=\"n gy gz ha hb hc hd he hf ab\">\n<div class=\"n o\">\n<div class=\"hg r ap\"><\/div>\n<\/div>\n<\/div>\n<div class=\"hg r ap\"><\/div>\n<div class=\"hh r ao\">\n<div class=\"hi\">\n<div>\n<div class=\"bf\" role=\"tooltip\" aria-hidden=\"true\" aria-describedby=\"1\" aria-labelledby=\"1\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<section class=\"fb fc fd fe ff\">\n<div class=\"hj\">\n<div class=\"n p\">\n<div class=\"hk hl hm hn ho hp ag hq ah hr aj ak\">\n<figure class=\"hs ht hu hv hw hj hx hy paragraph-image\">\n<div class=\"hz ia bl ib ak\">\n<div class=\"es et ai\">\n<div class=\"ig r bl ih\">\n<div class=\"ii r\">\n<div class=\"bk ic ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik cl qj\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/30\/1%2AbQMVkY6PN6I17P6d8P7fOw.png?resize=910%2C596&#038;ssl=1\" width=\"910\" height=\"596\" \/><\/div>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"oo qi ew t u id ak im\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/1001\/1%2AbQMVkY6PN6I17P6d8P7fOw.png?resize=910%2C596&#038;ssl=1\" width=\"910\" height=\"596\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/figure>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"n p\">\n<div class=\"ac ae af ag ah fg aj ak\">\n<p id=\"0cf5\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">For the upcoming release of <a class=\"bo dd jb jc jd je\" href=\"https:\/\/github.com\/lettier\/gifcurry\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">Gifcurry <\/a>\u2014 <a class=\"bo dd jb jc jd je\" href=\"https:\/\/lettier.github.io\/gifcurry\/\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">an open-source, video-to-GIF maker <\/a>\u2014 a completely custom theme was made using CSS and Haskell. The new theme will help maintain a more consistent look and feel across platforms and for Linux \u2014 across distros. The theme also ensures the need for only one set of custom icons.<\/p>\n<p id=\"d710\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">Creating a GTK desktop app and styling it like you would a web application is a great alternative to frameworks like Electron. You can reuse your CSS knowledge and ship a cross-platform product with reduced system requirements. Of course Haskell isn\u2019t required, as you can create and style GTK apps without it, but why would you want to? \ud83d\ude1b<\/p>\n<blockquote class=\"jf jg jh\">\n<p id=\"1cd5\" class=\"in io cn ji ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">The CSS-like syntax will hopefully be much more familiar to many people, lowering the barrier for custom theming. \u2014 <a class=\"bo dd jb jc jd je\" href=\"https:\/\/developer.gnome.org\/gtk3\/stable\/gtk-migrating-GtkStyleContext-css.html\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">https:\/\/developer.gnome.org\/gtk3\/stable\/gtk-migrating-GtkStyleContext-css.html<\/a><\/p>\n<\/blockquote>\n<h2 id=\"569f\" class=\"jj jk cn ar aq jl jm jn jo jp jq jr js jt ju jv jw\" data-selectable-paragraph=\"\">Supporting Older GTK Versions<\/h2>\n<\/div>\n<\/div>\n<div class=\"hj\">\n<div class=\"n p\">\n<div class=\"hk hl hm hn ho hp ag hq ah hr aj ak\">\n<figure class=\"hs ht hu hv hw hj hx hy paragraph-image\">\n<div class=\"hz ia bl ib ak\">\n<div class=\"es et ai\">\n<div class=\"ig r bl ih\">\n<div class=\"jx r\">\n<div class=\"bk ic ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik cl qj\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/30\/1%2AaRvb8QscmsbvDprDxGIJcQ.png?resize=910%2C425&#038;ssl=1\" width=\"910\" height=\"425\" \/><\/div>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"oo qi ew t u id ak im\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/1002\/1%2AaRvb8QscmsbvDprDxGIJcQ.png?resize=910%2C425&#038;ssl=1\" width=\"910\" height=\"425\" \/><\/div>\n<\/div>\n<\/div>\n<\/div><figcaption class=\"av bj jy jz ka eu es et kb kc aq ed\" data-selectable-paragraph=\"\">Gifcurry running on Ubuntu 14.04<\/figcaption><\/figure>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"n p\">\n<div class=\"ac ae af ag ah fg aj ak\">\n<p id=\"c22b\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">When building a custom theme for your GTK application, you\u2019ll have to consider supporting older versions of GTK. If you have Ubuntu 14.04 users for instance, you\u2019ll need to support the GTK 3.10 <a class=\"bo dd jb jc jd je\" href=\"https:\/\/developer.gnome.org\/gtk3\/stable\/gtk-migrating-GtkStyleContext-css.html\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">CSS file format<\/a>. Ubuntu 16.04 users will need support for GTK 3.18.<\/p>\n<p id=\"c53a\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\"><a class=\"bo dd jb jc jd je\" href=\"https:\/\/github.com\/lettier\/gifcurry\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">Gifcurry<\/a> aims to support GTK 3.10 all the way up to <a class=\"bo dd jb jc jd je\" href=\"https:\/\/blog.gtk.org\/2018\/06\/23\/a-gtk-3-update\/\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">GTK 3.24<\/a> and beyond. Between 3.10 and now, there are some commonalities but big changes occurred in 3.18, 3.19, and 3.20. <a class=\"bo dd jb jc jd je\" href=\"https:\/\/github.com\/lettier\/gifcurry\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">Gifcurry<\/a> applies a base styling and then detects the machine\u2019s GTK version applying more styling rules as the version number goes higher.<\/p>\n<p id=\"f659\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">Some of the big changes include:<\/p>\n<ul class=\"\">\n<li id=\"77c0\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja kd ke kf\" data-selectable-paragraph=\"\">Element selectors like <em class=\"ji\">GtkButton<\/em> turning into<em class=\"ji\"> button.<\/em><\/li>\n<li id=\"ddd0\" class=\"in io cn ar ip b iq kg is kh iu ki iw kj iy kk ja kd ke kf\" data-selectable-paragraph=\"\">Class selectors turning into element selectors like <em class=\"ji\">GtkSpinButton .entry<\/em> turning into <em class=\"ji\">spinbutton entry.<\/em><\/li>\n<li id=\"0760\" class=\"in io cn ar ip b iq kg is kh iu ki iw kj iy kk ja kd ke kf\" data-selectable-paragraph=\"\">Pseudo classes like <em class=\"ji\">:insensitive<\/em> becoming<em class=\"ji\"> :disabled.<\/em><\/li>\n<li id=\"d30c\" class=\"in io cn ar ip b iq kg is kh iu ki iw kj iy kk ja kd ke kf\" data-selectable-paragraph=\"\">Non-standard properties like <em class=\"ji\">-gtk-icon-shadow<\/em> added in later versions.<\/li>\n<\/ul>\n<p id=\"9a05\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">You can combine most of the differences in the same CSS file and there won\u2019t be any conflicts or exceptions raised.<\/p>\n<pre class=\"hs ht hu hv hw kl km du\"><span id=\"cfed\" class=\"jj jk cn ar kn b bj ko kp r kq\" data-selectable-paragraph=\"\">entry selection,\n.entry:selected {\n  background-color: #000;\n}<\/span><\/pre>\n<p id=\"67cc\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">GTK will raise exceptions, when parsing your CSS data, for situations like an unknown property or incorrect syntax. This of course doesn\u2019t happen for the web where you browser crashes because of a bad CSS file. Nonetheless, you should test any rules you add. GTK will issue warnings for deprecated rules so be sure to watch the standard streams.<\/p>\n<h2 id=\"effe\" class=\"jj jk cn ar aq jl jm jn jo jp jq jr js jt ju jv jw\" data-selectable-paragraph=\"\">Deciding which CSS Rules You Need<\/h2>\n<\/div>\n<\/div>\n<div class=\"hj\">\n<div class=\"n p\">\n<div class=\"hk hl hm hn ho hp ag hq ah hr aj ak\">\n<figure class=\"hs ht hu hv hw hj hx hy paragraph-image\">\n<div class=\"hz ia bl ib ak\">\n<div class=\"es et ai\">\n<div class=\"ig r bl ih\">\n<div class=\"ii r\">\n<div class=\"oo qi ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik il\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/freeze\/max\/30\/1%2A_wAkqx6DEG4ld9jsIz3FHA.gif?resize=910%2C596&#038;ssl=1\" width=\"910\" height=\"596\" \/><\/div>\n<p><img class=\"bk ic ew t u id ak im\" role=\"presentation\" width=\"1001\" height=\"656\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/figure>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"n p\">\n<div class=\"ac ae af ag ah fg aj ak\">\n<p id=\"3533\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">There are a lot of elements and states to consider when creating a whole theme. Depending on what widgets you use, you can get away with not specifying everything.<\/p>\n<p id=\"d39e\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">What I recommended is to select your favorite GTK theme as your system theme. Afterwards, start your app from the terminal specifying you want the interactive debugger.<\/p>\n<pre class=\"hs ht hu hv hw kl km du\"><span id=\"dc4f\" class=\"jj jk cn ar kn b bj ko kp r kq\" data-selectable-paragraph=\"\">$ GTK_DEBUG=interactive my-gtk-app<\/span><\/pre>\n<\/div>\n<\/div>\n<div class=\"hj\">\n<div class=\"n p\">\n<div class=\"hk hl hm hn ho hp ag hq ah hr aj ak\">\n<figure class=\"hs ht hu hv hw hj hx hy paragraph-image\">\n<div class=\"hz ia bl ib ak\">\n<div class=\"es et ai\">\n<div class=\"ig r bl ih\">\n<div class=\"kr r\">\n<div class=\"bk ic ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik cl qj\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/30\/1%2A2bDWEuChzgCdFXCEZEzDVQ.png?resize=910%2C246&#038;ssl=1\" width=\"910\" height=\"246\" \/><\/div>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"oo qi ew t u id ak im\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/1113\/1%2A2bDWEuChzgCdFXCEZEzDVQ.png?resize=910%2C246&#038;ssl=1\" width=\"910\" height=\"246\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/figure>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"n p\">\n<div class=\"ac ae af ag ah fg aj ak\">\n<p id=\"841c\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">This GUI debugger allows you to inspect any element\/node and see what its CSS properties, children, parent, style classes, state, and more are. You can change the theme on the fly and also update the CSS rules as the app is running. It is similar to Chrome\u2019s DevTools but you can\u2019t point-and-click update the CSS properties for example. To update the CSS, you\u2019ll have to click the CSS tab and write out your changes in the provided text box. As you write out your rules, it will underline any errors and\/or warnings.<\/p>\n<figure class=\"hs ht hu hv hw hj es et paragraph-image\">\n<div class=\"es et ks\">\n<div class=\"ig r bl ih\">\n<div class=\"kt r\">\n<div class=\"bk ic ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik cl qj\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/freeze\/max\/30\/1%2AXd95ZCEs7LS2UJPhxNv7YA.gif?resize=700%2C459&#038;ssl=1\" width=\"700\" height=\"459\" \/><\/div>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"oo qi ew t u id ak im\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/700\/1%2AXd95ZCEs7LS2UJPhxNv7YA.gif?resize=700%2C459&#038;ssl=1\" width=\"700\" height=\"459\" \/><\/div>\n<\/div>\n<\/div>\n<\/figure>\n<p id=\"6816\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">Using the interactive debugger, you can quickly build up the CSS rules that make up your theme. After you get it looking pixel perfect, select the visual tab and change the theme. Look for any breakages and patch your rules as needed. The more themes you test against, the more your app theme will look consistent no matter what the GTK system theme is.<\/p>\n<p id=\"4b2f\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">After you\u2019re finished with one GTK version, switch to another and continue this same workflow. I like to test GTK 3.22, 3.18, and 3.10 simultaneously using two virtual machines running Ubuntu 14.04 and Ubuntu 16.04.<\/p>\n<h2 id=\"4e51\" class=\"jj jk cn ar aq jl jm jn jo jp jq jr js jt ju jv jw\" data-selectable-paragraph=\"\">Generating the CSS Data<\/h2>\n<figure class=\"hs ht hu hv hw hj es et paragraph-image\">\n<div class=\"es et ks\">\n<div class=\"ig r bl ih\">\n<div class=\"kt r\">\n<div class=\"oo qi ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik il\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/freeze\/max\/30\/1%2AVrkp1bZEZFUmjxovJUsVYw.gif?resize=700%2C459&#038;ssl=1\" width=\"700\" height=\"459\" \/><\/div>\n<p><img class=\"bk ic ew t u id ak im\" role=\"presentation\" width=\"700\" height=\"459\" \/><\/div>\n<\/div>\n<\/div>\n<\/figure>\n<p id=\"1843\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">You have a few options for getting the CSS rules to GTK. You can write plain CSS, stick it in a <em class=\"ji\">.css<\/em> file, and apply it via the GTK API.<\/p>\n<pre class=\"hs ht hu hv hw kl km du\"><span id=\"86cb\" class=\"jj jk cn ar kn b bj ko kp r kq\" data-selectable-paragraph=\"\">button {\n   color: white;\n   background: #000;\n}<\/span><\/pre>\n<p id=\"1fc7\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">\u2026<\/p>\n<pre class=\"hs ht hu hv hw kl km du\"><span id=\"1d2a\" class=\"jj jk cn ar kn b bj ko kp r kq\" data-selectable-paragraph=\"\">styleFile \n  &lt;- getDataFileName \"data\/style.css\"<\/span><span id=\"a0f1\" class=\"jj jk cn ar kn b bj ku kv kw kx ky kp r kq\" data-selectable-paragraph=\"\">GI.Gtk.cssProviderLoadFromPath\n  provider\n  (Data.Text.pack styleFile)<\/span><span id=\"f0e3\" class=\"jj jk cn ar kn b bj ku kv kw kx ky kp r kq\" data-selectable-paragraph=\"\">GI.Gtk.styleContextAddProviderForScreen\n  screen\n  provider\n  cssPriority<\/span><\/pre>\n<p id=\"ecbb\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">Another option is to use your favorite CSS preprocessor, stick it into your build chain, generate the file, and then apply that via the GTK API. Since we\u2019re using Haskell, we can use <a class=\"bo dd jb jc jd je\" href=\"http:\/\/fvisser.nl\/clay\/\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">Clay<\/a> \u2014 \u201ca CSS preprocessor [\u2026] implemented as an embedded domain specific language (EDSL) in Haskell.\u201d<\/p>\n<p id=\"ad75\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">The nice thing about using Clay is that we don\u2019t have to stick it into the build process, generate a .css file, and load it via the GTK API. Clay allows us to stay in Haskell, generate the CSS rules at run time, and apply the rendered output via <em class=\"ji\">cssProviderLoadFromData<\/em>.<\/p>\n<pre class=\"hs ht hu hv hw kl km du\"><span id=\"9479\" class=\"jj jk cn ar kn b bj ko kp r kq\" data-selectable-paragraph=\"\">let css =\n        encodeUtf8 $\n          render $\n            button ? do\n              color white\n              background \"#000\"<\/span><span id=\"6b57\" class=\"jj jk cn ar kn b bj ku kv kw kx ky kp r kq\" data-selectable-paragraph=\"\">GI.Gtk.cssProviderLoadFromData\n    provider\n    css<\/span><span id=\"92b5\" class=\"jj jk cn ar kn b bj ku kv kw kx ky kp r kq\" data-selectable-paragraph=\"\">GI.Gtk.styleContextAddProviderForScreen\n    screen\n    provider\n    cssPriority<\/span><\/pre>\n<h2 id=\"e013\" class=\"jj jk cn ar aq jl jm jn jo jp jq jr js jt ju jv jw\" data-selectable-paragraph=\"\">The End<\/h2>\n<figure class=\"hs ht hu hv hw hj es et paragraph-image\">\n<div class=\"hz ia bl ib ak\">\n<div class=\"es et kz\">\n<div class=\"ig r bl ih\">\n<div class=\"la r\">\n<div class=\"bk ic ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik cl qj\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/freeze\/max\/30\/1%2AA3i-ujPUfWpPsv2oDjRTsw.gif?resize=887%2C582&#038;ssl=1\" width=\"887\" height=\"582\" \/><\/div>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"oo qi ew t u id ak im\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/887\/1%2AA3i-ujPUfWpPsv2oDjRTsw.gif?resize=887%2C582&#038;ssl=1\" width=\"887\" height=\"582\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/figure>\n<p id=\"c437\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">If you\u2019ve thought about creating a desktop app, GTK is a great solution that allows fully customizable styling. Using CSS, you\u2019re in complete control of the look, feel, and branding of your app just like you would be with the web. The CSS is slightly different but you\u2019ll hardly notice. And if you need something stronger than styling, you can always <a class=\"bo dd jb jc jd je\" href=\"https:\/\/codeburst.io\/how-to-build-a-custom-gtk-widget-with-haskell-eaff04a6262\" target=\"_blank\" rel=\"noopener noreferrer\">create your own custom widget<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/section>\n<hr class=\"lb ed lc bg ld ka le lf lg lh li\" \/>\n<section class=\"fb fc fd fe ff\">\n<div class=\"n p\">\n<div class=\"ac ae af ag ah fg aj ak\">\n<p id=\"3c18\" class=\"in io cn ar ip b iq ir is it iu iv iw ix iy iz ja\" data-selectable-paragraph=\"\">Interested in GTK apps? Take a look at <a class=\"bo dd jb jc jd je\" href=\"https:\/\/github.com\/lettier\/movie-monad\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">Movie Monad<\/a>. Need a video-to-GIF maker? Download <a class=\"bo dd jb jc jd je\" href=\"https:\/\/github.com\/lettier\/gifcurry\" target=\"_blank\" rel=\"noopener nofollow noreferrer\">Gifcurry<\/a>. Enjoyed this article? Show your support by clicking the applause button. Thanks!<\/p>\n<\/div>\n<\/div>\n<div class=\"hj\">\n<div class=\"n p\">\n<div class=\"hk hl hm hn ho hp ag hq ah hr aj ak\">\n<figure class=\"hs ht hu hv hw hj hx hy paragraph-image\">\n<div class=\"es et lj\">\n<div class=\"ig r bl ih\">\n<div class=\"lk r\">\n<div class=\"bk ic ew t u id ak bd ie if\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" class=\"ew t u id ak ij ik cl qj\" role=\"presentation\" src=\"https:\/\/i0.wp.com\/miro.medium.com\/max\/30\/1%2Ai3hPOj27LTt0ZPn5TQuhZg.png?resize=910%2C34&#038;ssl=1\" width=\"910\" height=\"34\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/figure>\n<\/div>\n<\/div>\n<\/div>\n<\/section>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p class=\"excerpt\">For the upcoming release of Gifcurry \u2014 an open-source, video-to-GIF maker \u2014 a completely custom theme was made using CSS and Haskell. The\u2026 Source: How to Style Your GTK App with CSS and Haskell &#8211; codeburst<\/p>\n<p class=\"more-link-p\"><a class=\"more-link\" href=\"https:\/\/monodes.com\/predaelli\/2020\/01\/01\/how-to-style-your-gtk-app-with-css-and-haskell-codeburst\/\">Read more &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"link","meta":{"inline_featured_image":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1],"tags":[258],"class_list":["post-6373","post","type-post","status-publish","format-link","hentry","category-senza-categoria","tag-gtk","post_format-post-format-link"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6daft-1EN","jetpack-related-posts":[{"id":9176,"url":"https:\/\/monodes.com\/predaelli\/2022\/03\/24\/the-end-of-the-nice-gtk-button\/","url_meta":{"origin":6373,"position":0},"title":"The end of the nice GTK button","author":"Paolo Redaelli","date":"2022-03-24","format":false,"excerpt":"A look into my favorite Adwaita button and why I'm not happy it's going away Source: The end of the nice GTK button","rel":"","context":"In &quot;Senza categoria&quot;","block_context":{"text":"Senza categoria","link":"https:\/\/monodes.com\/predaelli\/category\/senza-categoria\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":13995,"url":"https:\/\/monodes.com\/predaelli\/2025\/09\/14\/gtk-1\/","url_meta":{"origin":6373,"position":1},"title":"Gtk 1","author":"Paolo Redaelli","date":"2025-09-14","format":false,"excerpt":"Robin Rowe is maintaining GTK1, the classic GUI Library for Windows, Linux and MacOS. It may feel retro but its value lies in its simplicity. Currently it does has Wayland Support. It is currently hosted on GitLab but for precaution I copied it on codeberg.org\/tybor\/gtk1","rel":"","context":"In &quot;Gnome&quot;","block_context":{"text":"Gnome","link":"https:\/\/monodes.com\/predaelli\/category\/gnome\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":15420,"url":"https:\/\/monodes.com\/predaelli\/2026\/03\/31\/at-last\/","url_meta":{"origin":6373,"position":2},"title":"At last!","author":"Paolo Redaelli","date":"2026-03-31","format":"link","excerpt":"Firefox & Gtk Emoji\u00a0picker! https:\/\/mastransky.wordpress.com\/2026\/03\/20\/firefox-gtk-emoji-picker\/?page_id=1040","rel":"","context":"In &quot;Web&quot;","block_context":{"text":"Web","link":"https:\/\/monodes.com\/predaelli\/category\/web\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2194,"url":"https:\/\/monodes.com\/predaelli\/2017\/02\/21\/2194\/","url_meta":{"origin":6373,"position":3},"title":"Oh, my! WOGUE wroteFeb 21,\u2026","author":"Paolo Redaelli","date":"2017-02-21","format":false,"excerpt":"Oh, my! WOGUE wroteFeb 21, 11:22 you know guys, in the around 5 years I'm blogging about GNOME, you wont find not a single time of mine, proposing you to start with GNOME Development ..but I didn't either say dont do GNOME development! the reason isn't because GNOME development is\u2026","rel":"","context":"In &quot;Mood&quot;","block_context":{"text":"Mood","link":"https:\/\/monodes.com\/predaelli\/category\/mood\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/monodes.com\/predaelli\/wp-content\/uploads\/sites\/4\/2017\/02\/photo.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":12922,"url":"https:\/\/monodes.com\/predaelli\/2025\/03\/07\/network-transparency-with-wayland-bis\/","url_meta":{"origin":6373,"position":4},"title":"Network transparency with Wayland (bis)","author":"Paolo Redaelli","date":"2025-03-07","format":false,"excerpt":"Oh, I forgot I already wrote about waypipe, regarding 3d games. Almost 5 years ago! Now the boring part. If you log into a machine where a Wayland session is already active - I often log into my home machines remotely - any Gtk program will show on the Wayland\u2026","rel":"","context":"In &quot;Tricks&quot;","block_context":{"text":"Tricks","link":"https:\/\/monodes.com\/predaelli\/category\/documentations\/tricks\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":9041,"url":"https:\/\/monodes.com\/predaelli\/2022\/01\/04\/is-the-macos-gui-development-scene-really-this-bad\/","url_meta":{"origin":6373,"position":5},"title":"Is the macOS GUI development scene really this bad?","author":"Paolo Redaelli","date":"2022-01-04","format":false,"excerpt":"Paul WalkerFollow on last summer asked Is the macOS GUI development scene really this bad? That's a question I'm interested in because while I've been using exclusively Linux since 1997 I strive to write portable programs. Most people are still \"shackled\" into proprietary operative systems. He enlist Swift, JavaScript, C#,\u2026","rel":"","context":"In &quot;Senza categoria&quot;","block_context":{"text":"Senza categoria","link":"https:\/\/monodes.com\/predaelli\/category\/senza-categoria\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/6373","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/comments?post=6373"}],"version-history":[{"count":0,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/posts\/6373\/revisions"}],"wp:attachment":[{"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/media?parent=6373"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/categories?post=6373"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/monodes.com\/predaelli\/wp-json\/wp\/v2\/tags?post=6373"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}