Custom Properties The Secret Ingredients for CSS Magic 🎩🐇 • Today I want to talk about one of my favourite things in CSS, and that’s custom properties. I spend a lot of my spare time making demos and tinkering with CSS, and in nearly all my demos I find myself using custom properties these days. I’m finding new use cases for them all the time, and have a lot of fun working with them. So I want to share some of those use cases with you today. • You may have heard custom properties referred to as CSS variables, because they allow you to store values for reuse in your stylesheets.

:root { —primaryColor: deeppink; } • If you’re not too familiar with custom properties, this is a quick recap of the syntax • Here we’re defining a custom property (or variable) called primaryColor on the root element, making it a global variable that we can use anywhere in our stylesheet, and the value of our custom property is deeppink, the CSS named colour.

:root { —primaryColor: deeppink; } .box { background-color: var(—primaryColor); } • Then we can use that custom property inside a selector to apply our predefined value as the background colour of this square, say, or anywhere else we might want to use it.

:root { —primaryColor: deeppink; } .box { background-color: var(—primaryColor, darkorchid); } • We can also provide a default value if we choose to, in case there’s a possibility that we’re using a custom property before it’s been defined. • So if we’ve previously defined the custom property then our box is going to be pink…

/* :root { —primaryColor: deeppink; } */ .box { background-color: var(—primaryColor, darkorchid); } • But if we haven’t defined it yet it’s going to fall back to darkorchid as the background colour.

IE11 😢 .box { background-color: deeppink; background-color: var(—bg, darkorchid); } • Browsers that don’t support custom properties won’t recognise that background colour as a valid property value, so one way we could handle that is to set a valid background-color value higher up, and allow the browser to ignore the second rule if it doesn’t support it. • So in IE11, which doesn’t support custom properties, this box is going to be deeppink.

IE11 😢 .box { /* background-color: deeppink; */ background-color: var(—bg, darkorchid); } The square is here, you just can’t see it • But if we don’t include a fallback value and the browser doesn’t support custom properties, the background-color will fall back to its initial value, which is transparent – the default value is meaningless if the browser doesn’t understand it, which maybe goes without saying.

Nice lovely browser .box { background-color: deeppink; background-color: var(—bg, 300); } Sorry, it’s still invisible • However, if the browser does support custom properties and you put some kind of nonsensical value in there, like 300 – which makes no sense as a backgroundcolor value – this will also fall back to the initial value, even though we’ve set the background-color value higher up in our selector. Again, the element will be transparent. • So that’s something to watch out for

Sass variable $bgColor: deeppink; !== —primaryColor: deeppink; Custom property • Of course, we’ve been able to use variables for quite a long time in Sass and other preprocessor languages. But what makes custom properties different is that they’re dynamic variables, and can be updated in your CSS, or with Javascript. Preprocessor variables are compiled into regular CSS property values before your code hits the browser – the browser will never know what is and isn’t a variable. Custom properties are computed in the browser, and that has implications for how we can use them. • Let’s first of all take a look at a simple use case.

Repeating values Repeating values Repeating values Repeating values Repeating values Repeating values Repeating values • That’s repeating values, which is perhaps the most obvious use case for custom properties – anywhere you need to update a value that’s repeated throughout your code. • That could be shorthand property values, for example.

.some-element { padding: 12rem 1rem 1rem; } .another-element { padding: 0 1rem 1rem; } Suppose you have two elements that have different top padding values, but the same padding for the remaining sides…

.some-element { padding: 12rem 1rem 1rem; } .another-element { padding: 0 1rem 1rem; } @media (min-width: 50em) { .some-element { padding: 12rem 3rem 3rem; } } .another-element { padding: 0 3rem 3rem; } • …and at different breakpoints we need to adjust the padding of the three remaining sides identically across both elements. • That ends up being quite a lot of code to write.

.some-element { padding: 12rem var(—pad) var(—pad); } .another-element { padding: 0 var(—pad) var(—pad); } We could replace those identical values with a custom property…

:root { —pad: 1rem; } @media (min-width: 50em) { :root { —pad: 3rem; } } …and that way we only need to change one value within our media query, both values will be updated. And we could do that for multiple breakpoints if we needed to.

:root { —clip: polygon(80% 0, 100% 0, 100% 100%, 20% 100%); } .element { -webkit-clip-path: var(—clip); clip-path: var(—clip); } It’s pretty useful too for vendor prefixes, especially for long values. For example clip-path still needs to be prefixed in some browsers.

Global vs local scoping • In the examples so far I’m defining the custom property on the root element, so it’s a global variable. You can also scope a custom property to a selector, and we’ll look at some reasons to do that a little later on. • But defining globals variables can be a good idea if they’re part of a design system – if they’re global values that we need to use repeatedly across different components.

Dark mode • It’s becoming really popular for websites to have a dark theme, and using custom properties makes it quite easy to implement this. This is how I do it on my own website:

:root { —primary: deepPink; —headerBg: #1d1d26; —textColor: #0e0f0f; —textColorInverse: #fcfdff; —bg: #fcfdff; —bgTint: #dfeded; —bgGrey: #e6e8e8; —white: #fcfdff; } I have all my colours set as custom properties…

@media (prefers-color-scheme: dark) { :root { —bg: #161618; —bgTint: #27272c; —bgGrey: #27272c; —textColor: #dbd7db; —textColorInverse: #0e0f0f; } } …and then I use the prefers-color-scheme media query to redefine those variables for dark mode • I don’t have to change all of them because some, like the primary brand colour and header colour will remain the same. I only have to change these five custom properties and that’s my dark mode implemented right across the site. • But I do have to be quite careful about naming them – my instinct is to name variable things like bgLight or bgDark for the background colour, but of course when we switch them to dark mode we don’t want a value for bgLight that’s actually a dark colour, that would just be too confusing.

Vertical rhythm Another reason for global variables might be in order to maintain vertical rhythm, and consistent spacing.

—vr: 2rem • We could set a custom property for one vertical spacing unit – so we’re going to call this vertical rhythm custom property “vr”, and set its value as 2rem.

var(—vr) • These highlighted margins are each one vr, which in this case is 2rem

calc(2 * var(—vr)) • And these spaces are 2 times our vertical spacing unit, so we can use calc for that

      • { margin-top: var(—vr); } We could do that quite easily with the lobotomised owl selector (coined by Heydon Pickering), which uses the adjacent sibling combinator. So here we’re saying that any element that follows another element should have a standard top margin with a value of one “vr” (2rems).

      • { margin-top: var(—vr); } * + h2 { margin-top: calc(2 * var(—vr)); } But for some element’s we’ll override that and give them a top margin of a multiple of a vr unit.

:root { —vr: 1rem; } @media (min-width: 50em) { :root { —vr: 2rem; } } • And perhaps we want that space to scale at different breakpoints, so we give elements a bit more breathing room on larger screens by increasing the whitespace. • Where we’re setting that custom property, we could specify that at a certain breakpoint we want to increase that vr unit across the whole site

—vr: 1rem; —vr: 2rem; So the large spaces will always be twice the width of the smaller ones whatever the size of our screen – and we only need that one media query.

:root { —vr: 1rem; —2vr: calc(2 * var(—vr)); } @media (min-width: 50em) { :root { —vr: 2rem; } } • For even more consistency we could even abstract that calc value out into another custom property. • We don’t need to recalculate that 2vr variable in a media query, that’s going to be automatically updated for us because it’s calculated using the vr custom property, which we’re already updating. • So we can calculate custom property values from other custom properties, which can be very useful.

Color One example that has already been written and talked about quite a bit is colour. I really like using HSL when I’m working with custom properties…

Hue Saturation Lightness hsl(3deg, 89%, 61%) • Which stands for Hue, Saturation, Lightness. • I’m not going to go into too much detail on this one, because it’s already fairly widely documented.

Hue Saturation Lightness hsl(var(—hue), 89%, 61%) • But setting variables for each of the values in HSL gives you a fine degree of control over colour. • In this example I’m using a custom property for the hue value.

.color-pair { —hue: 3deg; —hue2: calc(var(—hue) + 180deg); } I’m setting the ‘hue’ value as 3 degrees, which corresponds to a red hue. And then I’m setting this second custom property, ‘—hue2’, which I’m calculating as the original hue plus 180 degrees.

—hue: 3deg; —hue2: calc(var(—hue) + 180deg); • So that corresponds to a hue value from the opposite side of the colour wheel • And that’s going to give us a complementary colour pair.

.color-pair—red { —hue: 3deg; } hsl(var(—hue), 89%, 61%); hsl(var(—hue2), 89%, 61%); codepen.io/michellebarker/pen/vqgaLL So we could create a bunch of colour pairings and adjust them as much as we want just by tweaking one value…

.color-pair—purple { —hue: 276deg; } hsl(var(—hue), 89%, 61%); hsl(var(—hue2), 89%, 61%); codepen.io/michellebarker/pen/vqgaLL …the secondary colour will always respond and be the exact same number of degrees from the first. We don’t need to recalculate that second colour each time.

.color-pair—yellow { —hue: 65deg; } hsl(var(—hue), 89%, 61%); hsl(var(—hue2), 89%, 61%); codepen.io/michellebarker/pen/vqgaLL

• This is in fact a single-div CSS illustration of a colour wheel, made using only CSS gradients (conic-gradient) and the mask-image property. • It uses custom properties in a similar way, to determine the hue value of each segment.

.element { —segment: 30deg; } Each segment is 30 degrees, which we can set as a custom property.

.element { —segment: 30deg; } Each gradient stop is calculated as a multiple of that custom property. calc(var(—segment) * 2);

.element { —segment: 30deg; } calc(var(—segment) * 3);

Relative values What we’re doing here is setting custom properties that are relative to another. And that doesn’t have to apply just to colour.

codepen.io/michellebarker/pen/yLLGVMQ Here’s a demo I made a little while ago that uses two clip paths.

Path 1 Path 2 codepen.io/michellebarker/pen/yLLGVMQ With an overlay you can see those two clip paths a bit more easily.

codepen.io/michellebarker/pen/yLLGVMQ The second clip-path needs to be offset from the first by the same amount in two places.

calc(80% - 0.5rem) calc(80% + 0.5rem) codepen.io/michellebarker/pen/yLLGVMQ So we have two x values for our clip path points that are separated by the same amount at the top…

calc(20% - 0.5rem) calc(20% + 0.5rem) codepen.io/michellebarker/pen/yLLGVMQ …and at the bottom • Without custom properties, if I update one path I then have to work out the revised path from the other, which starts to be a bit of a headache.

.element { —bottom: 20%; —top: 80%; —offset: 0.5rem; } codepen.io/michellebarker/pen/yLLGVMQ • But we could set some custom properties for those variables • We’re setting variables for the x-axis value for where we want to clip the elements at the top and bottom, and the offset value, for the distance between the two paths.

.element { —bottom: 20%; —top: 80%; —offset: 0.5rem; } calc(var(—top) - var(—offset)) calc(var(—top) + var(—offset)) codepen.io/michellebarker/pen/yLLGVMQ • Then we can calculate the x-value of the second path relative to the first and have that update automatically whenever I adjust one of those x-axis values.

.element { —bottom: 20%; —top: 80%; —offset: 0.5rem; } calc(var(—bottom) - var(—offset)) calc(var(—bottom) + var(—offset)) codepen.io/michellebarker/pen/yLLGVMQ • If we want to change the position of the clip-path, or we want to have a bigger gap between them, all we need to do is change the custom property, we don’t need to update the clip-path property value at all.

Local scoping • So far we’ve talked about using globally scoped custom properties, but in the last couple of examples you might’ve noticed we’re scoping them to a selector, and there are plenty of cases when that’s more useful. • One reason you might want to do that is to avoid a naming clash, which might mean a selector inadvertently inherits a custom property value you’ve set on an ancestor. Personally I find it helpful to set custom properties as close as possible to the point I’m using them. • Setting a custom property on a selector means it is inherited by its descendants. • There are also possible performance issues with using a lot of custom properties on the root element. Updating a custom property will cause a style recalculation of its descendants. So setting only where you need to use it is preferable.

.grid { —cols: 8; } grid-template-columns: repeat(var(—cols), minmax(0, 1fr)); … @media (min-width: 60em) { .grid { —cols: 12; } } • An example is a layout class. • Here we’re setting a custom property called cols for the number of columns in a grid layout, then updating it within the media query to avoid having to write out the entire grid-template-columns declaration again • cols doesn’t need to be a global variable because it only applies to our grid, and it means that if we have another layout class somewhere else in my code then we’re free to use the same variable name. • I like to use custom properties quite a lot with grid layout. One reason is because I sometimes work with quite complex grids, and I find that using custom properties can make them more maintainable.

• This is a screenshot for website I worked on a couple of years ago for Warners Bros Studios, and it was one of the first sites I built with CSS Grid. • The design used several different grids across the site. These components in particular used a 24-column grid, and there were 9 or 10 different variants, with the image and text positioned differently for each one.

• With the Firefox grid inspector turned on, you can see that 24-column grid • And there were 9 or 10 variants of these grid components, with the image and text laid out slightly differently in each one.

.grid { display: grid; grid-template-columns: [outer-start] minmax(20px, 1fr) [wrapper-start] repeat(24, minmax(0, 30px)) [wrapper-end] minmax(20px, 1fr) [outer-end]; grid-template-rows: [outer-start] 1fr [text-top-end] 40px [heading-start] auto [heading-end] 40px [text-bottom-start] 1fr [outer-end]; grid-gap: 0 20px; } • You can see the code where I’m defining my grid is fairly complex…

.grid { —columnWidth: 30px; —gutter: 20px; } display: grid; … • But using custom properties, we can define the important values at the top of the selector, and replace some of those values • And I find that it makes that grid easier to understand to someone new to it, or if you’re coming back to it after a while. • The other reason I like to use custom properties with Grid is because they have a very similar level of browser support – so it basically means if you’re testing for one, you’re testing for both. I can’t think of any browsers that support one and not the other, but I could be wrong about that.

@supports (—cols: 8) { .grid { —cols: 8; } We can test for support using feature queries, like this } /* More code here… */

S t a g g e r e d Animations • As I touched upon with colours, using calc with custom properties opens up a whole new world of excitement. • One place you can have a lot of fun with custom properties is staggered animations.

• Suppose we have a group of elements and we want them to animate in, but we want a slight delay between the start of each element’s animation, to create a staggered effect. • We can use custom properties to achieve this

.some-element:nth-child(2) { —i: 2; } .some-element:nth-child(3) { —i: 3; } • First we assign each element a custom property that corresponds to the element’s index. • We don’t need to assign the first one, because we can use a default value, which, if you remember, it will fall back to if the variable isn’t yet defined.

.some-element { —delay: calc(var(—i, 1) * 400ms); } Then we can use calc to calculate the delay between each one, and assign it to its own custom property.

.some-element { —delay: calc(var(—i, 1) * 400ms); animation: appear 1000ms var(—delay) forwards; } • …And we can use that in place of our animation-delay value in the shorthand property • If you’re working with lists, or any kind of UI where you have groups of similar elements, using an element’s index as a custom property can be very handy.

Progress indicators • Just to digress from the animation angle for a moment… • We can use a similar technique to visualise progress, which is something I did on a recent project.

• I needed to build a UI with a series of steps showing the customer journey, and at the top of each step is a little bar to indicate the step’s position relative to the total number of steps – so if you were at step 5 out of 10, the little progress bar would be 50% filled.

<ol style=”—length: 5”> <li style=”—i: 1”>…</li> <li style=”—i: 2”>…</li> <li style=”—i: 3”>…</li> <li style=”—i: 4”>…</li> <li style=”—i: 5”>…</li> </ol> • I gave each list item a custom property that corresponds to its index in the list, the whole list has a custom property that corresponds to the length of the list (or the number of items). • In this case I’m doing that in the HTML rather than in the CSS • The advantage of that is if you’re using a templating language, you can set the custom property within the loop – if you’re looping over items, you can use the loop index. • I use Twig, but most templating languages will give you a way to do this.

li::before { —stop: calc(100% / var(—length) * var(—i)); } background: linear-gradient(to right, var(—c1) var(—stop), var(—c2) var(—stop)); • There are difference ways you could style up that progress bar, but the way I’m doing it is by using a linear-gradient background with hard colour stops. • In the CSS, I calculated the gradient stop as a percentage, using the length and item index custom properties. This is step 2 of 5, so that progress bar is two-fifths filled. • Armed with a custom property value that matches the element’s index we can get even more creative, especially with text.

<h1> <span <span <span <span <span <span <span <span <span </h1> style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: style=”—i: 1”>B</span> 2”>r</span> 3”>e</span> 4”>a</span> 5”>t</span> 6”>h</span> 7”>i</span> 8”>n</span> 9”>g</span> In order to implement a staggered animation for a string of text, we need to wrap each individual letter in a `span` and assign it a custom property, which, if we prefer, we could do within the HTML itself

span { —delay: calc((var(—i) + 1) * 400ms); } animation: breathe 4000ms var(—delay) infinite both; • Then we can implement the animation in the same way as before, by calculating the delay variable • In this case the keyframe animation is animating the font weight of a variable font

⚠ • There are accessibility implications with wrapping each letter in a span, as some screenreaders won’t read that h1 as a whole world, instead they’ll announce every letter. So you need to make sure you account for that, which you can do by using aria-label or aria-labelledby, or a visually hidden element with the original text. • There are advantages and disadvantages to each approach

splitting.js.org • If you feel like setting custom property on every element could get a little laborious, there is a little JS library called Splitting.js that does that work for you, and that’s in fact what I used for the variable font demo. It’s pretty much my favourite JS library for using in demos. It also provides you with a custom property for the length of the text string too. • Let’s look at a few examples of how we can use these.

Motion Path I really love playing around with CSS Motion Path. The Motion Path specification enables us to place elements on a path, and to animate them along that path.

⚠ Motion Path • Warning: There is quite a lot of motion in the next section, so if you’re someone who suffers from sickness or vestibular disorders you might want to skip this part, or proceed with caution

.element { offset-path: path(‘M.4 84.1s127.4 188 267.7 0 247.3 0 247.3 0’); offset-distance: 0; } • We do that using the offset-path property to define a path, and use offset-distance to specify how far along the path to place an element. • For offset-path we give it a path value similar to SVG path syntax. That’s how I get my paths – draw them as SVGs and copy the value. • An offset-distance value of 0 would place an element at the start of the path.

.element { offset-path: path(‘M.4 84.1s127.4 188 267.7 0 247.3 0 247.3 0’); offset-distance: 0; animation: move 2000ms infinite alternate; } @keyframes move { to { offset-distance: 100%; } } • Then we can animate the offset-distance property value

codepen.io/michellebarker/pen/wvMvRLy • And that’s going to give us this little ball sliding up and down the curve. • So, a fairly simple animation, but kind of cool that we can do that just with CSS • Now, the name Motion Path is a little misleading, as it doesn’t necessarily have to relate to motion. We can position stationery elements along the path if we choose to.

.element { offset-distance: calc(var(—i, 1) * 4rem); } codepen.io/michellebarker/pen/PoZoVPE • If we want to position several elements equidistantly along the path then custom properties are great for that. • Again, we’re calculating the offset-distance value of each celement using the element’s index as a custom property • We can even do it with text.

.char { offset-distance: calc(var(—i, 1) * 2rem); } codepen.io/michellebarker/pen/abdbXyZ • This works best with uppercase text, a monospace font, or a font where each character has a very similar with, otherwise the spacing gets all weird

.char { —offset: calc(100% / (var(—char-total) + 1)); } offset-distance: calc(var(—offset) * var(—char-index)); • If we want to get even more clever about it, we could ensure that our text is always positioned along the full length of the path…

.char { —offset: calc(100% / (var(—char-total) + 1)); } offset-distance: calc(var(—offset) * var(—char-index)); • …by calculating the position of each character based on a custom property that corresponds to the length of the text string.

.char { —offset: calc(100% / (var(—char-total) + 1)); } offset-distance: calc(var(—offset) * var(—char-index)); • Again, Splitting.js provides us with that, with this custom property

codepen.io/michellebarker/pen/jOWOdxj • So we could position our text around a circle using offset-path, for instance. • We could, of course, position text in a circle using transforms, but the difference here is the text remains selectable, which is pretty cool.

—offset: calc(100% / (var(—char-total) + 1)); codepen.io/michellebarker/pen/jOWOdxj • We’re dividing the path length by the number of characters plus one – which is in case we want a space after our text ends – to get the offset value for each character

offset-distance: calc(var(—offset) * var(—char-index)); codepen.io/michellebarker/pen/jOWOdxj • And multiplying that by the character index in our offset-distance property. • So even if we change the length of our text, it will still fit the circle

.char { —position: calc(var(—char-index) * 1em); } offset-distance: var(—position); animation: move 1500ms infinite alternate forwards; @keyframes move { to { offset-distance: calc(var(—position) + 12rem); } } • That’s a static text example, but we can animate our text along a path too. • Here, our keyframe animation is moving each character along 12rem from its original position, which is calculated with the custom property

codepen.io/michellebarker/pen/abOKPyg Which gives us this result

.char { —delay: calc(var(—char-index) * 30ms); } animation: move 1500ms var(—delay) infinite alternate forwards; • That’s kind of cool, but we could also add a small delay to each character, using the same technique as the staggered animation before. • And that gives our animation a much more fluid, organic feel.

codepen.io/michellebarker/pen/abOKPyg …which I think feels a lot nicer

codepen.io/michellebarker/pen/XWJyydY And of course we can get even more creative with our paths and the properties we animate. But this demo is using exactly the same techniques, nothing more advanced than that.

codepen.io/michellebarker/pen/VwYOvJG And this one’s pretty similar, but with the addition of a 3d transform.

.item { —d: 1000ms; —i: calc(var(—item-total) - var(—item-index)); —x: calc((var(—item-index) - var(—i)) * 4deg); —tPerSegment: calc(var(—d) / var(—item-total)); —itemDuration: calc(var(—tPerSegment) * var(—item-index)); —itemDuration2: calc(var(—tPerSegment) * var(—i)); —c: calc(1 / var(—item-total) * 50); —h: calc(var(—item-index) * 15); —color: hsl(var(—h), 50%, 50%); —offset: calc(var(—item-index) * (var(—c) * 1%) + 1.65rem); —offset2: calc(var(—i) * (var(—c) * 1%)); —endPos: calc(var(—item-total) * (var(—c) * 1%)); } codepen.io/michellebarker/pen/VwYOvJG • But you can get pretty complex with custom properties if you want to, and calculate a bunch of things. • In the following demo I have these 12 custom properties, some of which are calculated from others.

codepen.io/michellebarker/pen/QWbGvWj • This is one of the more complex demos and those custom properties are computing the values for, delay, colour, 3d transform rotation value, the position along the path (offset distance – because it uses motion path), and animation duration, on each of those individual segments. • So there’s really no limit to how creative you can get if you use your imagination, and you like doing maths – which I apparently do!

codepen.io/michellebarker/pen/abdKLLz • We can also manipulate custom property values with Javascript. • This is a tool I built for generating zig-zag gradient backgrounds. • Whenever the user moves one of these sliders, it changes one of the custom properties, which I’m using to set the thickness of the stripes, the angle, and the width of the zig-zags. • Also, although we can’t use animations or transitions on CSS gradients, we can transition custom properties using Houdini …a set of APIs that allow developers to extend CSS.

✨ Houdini ✨ transition: —angle 200ms, —t 200ms, —w 200ms; • So I register the property in Javascript, and then transition it just like any other CSS property, which makes the whole thing feel a bit smoother. • So that’s another bonus you get with using custom properties, although it’s not supported in all browsers.

codepen.io/michellebarker/pen/abNZjLB To demonstrate how to set custom properties in JS, this is a simple demo that uses custom properties that correspond to the fibonacci sequence (or golden ratio).

items.forEach((el, i) => { el.style.setProperty(‘—f’, fibonacci[i]) el.style.setProperty(‘—i’, i) }) codepen.io/michellebarker/pen/abNZjLB • We can use element.style.setProperty to set them with JS • In a forEach loop, I’m setting a custom property on each item that uses the element’s index, which I’m using to calculate the colour • and another custom property for the corresponding value in the fibonacci sequence, which calculates the width of each item.

A user’s guide to CSS variables by Lea Verou bit.ly/3fSZCOS Calculating Color: Dynamic Color Theming with Pure CSS by Una Kravets bit.ly/3akbTKP CSS { In Real Life } css-irl.info I hope this has given you a bit of a glimpse into custom properties, and left you feeling a little bit inspired to play around with them and build some fun things. If you’d like more resources: • Lea Verou recently wrote a great introduction to custom properties. • Una Kravets has a great write up of using hsl and custom properties for colour theming. • And I’ve written about some of the techniques used here on my blog CSS {IRL}.

@MicheBarks @CSSInRealLife Thank you for listening, you can also find me on Twitter