Loading...

Top
PFQ Banner

This is PokéFarm Q, a free online Pokémon collectables game.

Already a user? New to PFQ?

BBCode and CSS Hacks

Forum Index > PokéFarm > Guides >

Kieri's AvatarKieri
Kieri's Avatar
Use the headings below to swap between the sections!
  • Introduction
  • LESS Basics
  • Getting Local Time
Hey! This guide is designed mostly to document some of the BBCode-, CSS- and
LESS
LESS is an extension to CSS that the site implements
-related
hacks I've run into getting my forum signature to work. In particular, keeping your signature BBCode very compressed, and setting it up to dynamically update (mine updates with time of day, for example). Throughout this guide I'll be running through my signature LESS to motivate some of the stuff I discuss, and how it could be used, which will make the guide a little linear in design, but I'll explain each of them in isolation as well so it won't be strictly necessary to read the whole thing.
This guide is formatted less as a "here's what you do," and more of a "here's how you come to the solution," which means it's a lot longer than you might want, but the aim is to help you figure out the process behind coming to the solution first and foremost. Hopefully, this can help someone develop some problem-solving skills that they can apply to a wider array of problems they might run into... If you do end up running into any mistakes, please let me know~
This guide expects readers to be familiar with quite a few things. This is not a guide to CSS, or really a guide to LESS. I'll introduce some things, but it's mostly here to point out some of the cool things you can do, or explain how you can implement some things that are difficult due to constraints on the LESS engine in use/the way the site handles CSS. It's up to you to figure out the details behind implementing these in your own styles, though I'm happy to help out or brainstorm with you if you get stuck on something. Really, you can think of this as a train of thought: what I wanted, the steps needed to get there, and how to deal with the issues that pop up throughout it. The specific fixes I use (like variable deference) can be applied outside the narrow scope of the examples here, so have fun with it!
There may be smarter ways to approach of lot of this, and I'd love to hear if you've got a better way to do anything! Honestly, I just found the first hack that worked and didn't make me want to gouge my eyes out and went with it...
So let's quickly run through some basic LESS, that we'll use throughout this. We'll develop a bit more later down the line, but you'll see me use these a fair bit, so I'll just mention it all at the start.
  • Variables as selectors
  • Verbatim strings
  • Nested selectors
  • "If" selectors
First up, using variables as selectors. We can give a variable a class name as a value, and use that as a basis to name some things. This is extremely useful when you want to minimise the amount of BBCode in your signature by removing class names, and using descendant selectors. For example, consider the following snippet:
BBCode
[style] .content { width: 100%; background-color: black; } .text { color: white } [/style] [sc=content] [sc=text]Hello![/sc] [/sc]
Output
Hello!
Now, if I wanted to cut down on the number of characters inside the BBCode, I could do two things: reduce the class name lengths, and refer to the
.text
class as
.content > div
inside the CSS. Of course, that would make things harder to read, so I can instead put the names into variables, and use those to refer to parts instead!
BBCode
[style] @content=c; @text=@content + ' > div'; .@{content} { width: 100%; background-color: black; } .@{text} { color: white } [/style] [sc=c] [sc]Hello![/sc] [/sc]
Output
Hello!
Beautiful! And you'll notice we can add to other variables to construct more complicated chains of selectors, as well. Really, this means you can define only the containing class, and all the other elements can be modified relative to it! In fact, if you really want to select the main container element, you can leave the variable
@blank
blank, and apply a style to just
@{blank}
! The pre-processing will prefix this with the post/signature/whatever's unique ID and you can apply styles to the box itself! In fact, I used that on this post to change the
white-space
property without needing to add a container to the whole post.
LESS can try to be a little too smart about things sometimes. It tries to do calculations on its own behind the scenes, and can end up kind of ruining your hand-crafted calculations. For a clear example, let's say you were trying to move an element to the right hand side of a container, but you only wanted to use the
left
property to do so (for some reason), so were using
calc()
. Let's see what the result is:
BBCode
[style] @box-dimensions=10px; .container { position: relative; width: 25px; height: 25px; background-color: blue; } .box { position: absolute; width: @box-dimensions; height: @box-dimensions; top: 5px; left: calc(100% - @box-dimensions); background-color: red; } [/style] [sc=container] [sc=box][/sc] [/sc]
Output
That's... not right... If we take a look at what the actual value of the
left
property is, we'll find that it has suddenly become
calc(90%)
, which isn't what we want at all! What's happened here is LESS has decided to try and calculate
100% - 10px
, and since it's kind of blind to units, it just subtracted the numbers, and used
%
as the units for both! This isn't what we want, so we need to force it to leave the calculation alone, using the
~
operator:
BBCode
[style] @box-dimensions=10px; .container { position: relative; width: 25px; height: 25px; background-color: blue; } .box { position: absolute; width: @box-dimensions; height: @box-dimensions; top: 5px; left: ~'calc(100% - @{box-dimensions})'; background-color: red; } [/style] [sc=container] [sc=box][/sc] [/sc]
Output
Much better! What
~
does is inserts verbatim whatever is inside the string that follows it, without running extra calculations or anything else.
calc()
is smart enough to deal with units (since it's handled by the browser), but LESS isn't, so we need to make sure it remains uncalculated in the CSS. You may also notice we've used
@{box-dimensions}
here inside the string; this inserts the value of the variable into the string. The
~
operator doesn't really keep everything verbatim in this regard, but what it's really doing is creating a string and then stripping off the quotations to leave the contents bare inside the CSS.
Now, let's talk nesting. One of the biggest features (or at least most commonly used features) of LESS is nesting, which allows you to nest selectors inside one another to combine them. This can be useful if you have multiple selectors you need to scope inside one, or even better, if you have multiple selectors and you need to select a common sub-selector. Now that's a little bit hard to read, so let's take a look at an example:
BBCode
[style] .box { width: 10px; height: 10px; background-color: green; } .container { background-color: red; width: 25px; height: 25px; .box { background-color: blue; } } [/style] [sc=container] [sc=box][/sc] [/sc] [sc=box][/sc]
Output
So the
.box
inside the
.container
has its
background-color
change! This is actually equivalent to moving it outside the
.container
and changing the selector to
.container .box
. Sometimes we want to be able to chain them with something like
>
or a pseudo-selector. We can do this using
&
:
BBCode
[style] .box { width: 10px; height: 10px; background-color: white; .box { width: 5px; height: 5px; } } .container { background-color: red; width: 50px; height: 25px; & > .box { background-color: blue; display: inline-block; &:first-of-type { background-color: green; } } } [/style] [sc=container] [sc=box] [sc=box][/sc] [/sc] [sc=box][/sc] [sc=box][/sc] [/sc]
Output
In this, we end up with a selector
.container > .box:first-of-type
with a green background, which matches only the first level of
.box
es, and of those, only the first. And finally, we can nest within multiple selectors as well:
BBCode
[style] .box { width: 10px; height: 10px; } .container-1, .container-2, .container-3 { background-color: red; width: 50px; height: 25px; display: inline-block; & > .box { background-color: blue; } } [/style] [sc=container-1] [sc=box][/sc] [/sc] [sc=container-2] [sc=box][/sc] [/sc] [sc=container-3] [sc=box][/sc] [/sc]
Output
here we select all three containers separately, and set their child
.box
elements' backgrounds to be blue.
Unfortunately, the version of LESS PFQ implements doesn't include the
if()
function, so our job is a little more difficult... There's a few ways around this, but one relatively simple way that avoids bulking up the BBCode with a bunch of elements is to use differences. It looks a little bit ugly in the CSS, but as long as we're reducing the size of our BBCode, all is well! To do this, we can take the difference of a value with each of its expected possible values. For example, suppose we want the colour of an element to be blue if a variable is
5
, and red otherwise:
BBCode
[style] @variable1=5; @variable2=6; @diff-5=@variable1 - 5; @diff-6=@variable2 - 5; .container-50, .container-60 { box-sizing: border-box; width: 100%; height: 50px; padding: 5px; border: 1px solid currentcolor; } .container-5@{diff-5}, .container-6@{diff-6} { border-width: 5px; } [/style] [sc=container-50]Example with variable=5[/sc] [br] [sc=container-60]Example with variable=6[/sc]
Output
Example with variable=5

Example with variable=6
What's happening here is a little subtle, but basically, if the variable matches whatever we're comparing against (in this case,
5
), then the corresponding
@diff-*
variable will be zero, so
.container-*@{diff-*}
will end up becoming
.container-*0
, which matches the class of the element! On the other hand, if the value were to be six, or anything else, you'd end up with the selector trying to target a non-existent class. In this case,
@diff-6
ends up being
1
so the second selector becomes
.container-61
, which doesn't match any element's class. We can use this to great effect later on, but for now, this is the general approach we'll take when we need to mimic an
if()
.
So, the thing this guide is based off of is a dynamic signature that changed based on the time of day. To do this, of course, I need to use the
[datetime]
BBCode. However, using the BBCode on its own is a little difficult, for a few reasons...
  • Getting my local time
  • Using the time in CSS
  • Modular Arithmetic
The
[datetime]
BBCode is designed to display the time provided in server time. This is preferred, since you can put in a local time, and it will display the server time for it without you needing to do any calculations of your own. For example:
BBCode
[datetime=H:i:s]now[/datetime]
Output
06:59:00
For reference, the parameter value (the
H:i:s
) can vary a lot, as we'll see soon, but it follows the standard PHP format, which can be found here. Similarly, the actual date/time format (the
now
) has a lot of possible values, which can be found here.
Of particular use, is the ability to change your timezone. So, if I wanted to include the server time for, say 1pm my time, I could do:
BBCode
[datetime=H:i:s]1pm Australia/Sydney[/datetime]
Output
03:00:00
This, however, isn't my local time, it's converting my time to the server time. To do the opposite we need to be a little smart about it... In particular, we're going to use a specific format for the timezone: the offset. My time is
, which is GMT+
. That is, my time is the server time (which is GMT) plus
hours. If we try this, though, we don't get what we want...
BBCode
[datetime=H:i:s]now +1000[/datetime]
Output
20:59:00
This... isn't right... It's not 8:59pm right now, it's 4:59pm!
The reason is, of course, that we've asked the server to convert from my local time to the server time, so it's subtracted
hours instead... We can, of course, just swap that around instead:
BBCode
[datetime=H:i:s]now -1000[/datetime]
Output
16:59:00
That's better! Now, for some, that might be enough, but we've forgotten one thing: daylight savings time! Right now, I might be in GMT+
, but once daylight savings
s, I'll be in GMT+
! Adjusting for this is a little difficult, but not impossible. In particular, we can use Unix timestamps to achieve the desired result.
The Unix timestamp is the number of seconds since the epoch (midnight on the 1st of January, 1970). If we take the Unix timestamp of the server, and of my local time, we can divide by 3600 to get the number of hours ahead of the server I am! Of course, to get the local time I'd need to know what timezone I'm in, right? Well, we can actually use region names instead! This doesn't work above, because we need to kind of "reverse" the region to get the calculations to work out, but here it works fine! For example, my current timezone is:
BBCode
GMT+[math]([datetime=U]now[/datetime] - [datetime=U]now Australia/Sydney[/datetime])/3600[/math]
Output
GMT+10
So... Just flip that
+
to a
-
and stick it in another
[datetime]
tag and we get:
BBCode
[datetime=H:i:s]now -[math]([datetime=U]now[/datetime] - [datetime=U]now Australia/Sydney[/datetime])/3600[/math]00[/datetime]
Output
16:59:00
And there we go, it is indeed 4:59pm right now, and that will adjust accordingly when we hit daylight savings! (well, within a day; there's some quirks with the calculations)
One last thing... We need to include this in a forum signature, so ideally it has to be pretty small... One way we can do this is to abuse some of the quirks of PHP time formats! In particular,
now
isn't actually necessary, and to add the
-
in automatically, I can just swap the two timestamps. We can also remove the
00
, since PHP is fine with just
-10
instead of
-1000
, like so:
BBCode
[datetime=H:i:s][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime]
Output
16:59:00
And there we have it! The last note I'll add: although
now
isn't needed, you still need to add something to a
[datetime]
tag's body, so we leave a single space on its own and it works fine!
If you live elsewhere, I'll leave it up to you to try and formulate a line for your timezone... Things flip if you live in a negative offset timezone, and if you live near GMT itself, they can get a little complicated once you bring DST into the mix... You can find PHP's list of supported regions here, or if you want to find some instead by offset (for testing, maybe), you can check the easily-navigable list of timezones on Wikipedia here.
Now that we have the time we want, we can try and get it into our CSS to use. To do so, we'll be using LESS variables. Thankfully, this is pretty straightforward.
For the rest of this, we'll be splitting our CSS out into a skin. The reason being that as we start adding to it, we'll end up bulking up the
[style]
in our signature too much and very quickly hit the character limit. There's other guides (like this one) for how to set things up, but the main idea is to make a skin with a small name (four characters) so you can import it into your signature, and keep all your CSS in the skin, wherever possible. Not everything can remain in the skin, however: the BBCode must remain in the signature, and some other stuff we'll touch on later on.
So... first order of business... turning the date/time into a useable value. For my purposes, we only need the time of day, and we only really care about the minute of the day, so we can just calculate the number of minutes past midnight:
BBCode
[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]
Output
1019
Since it's currently 4:59pm, we can confirm that
16*60+59=1019
! We can very easily push this into a variable to use elsewhere, without needing to copy-paste the BBCode all the time:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; .content:before { content: '' + @time; } [/style] [sc=content][/sc]
Output
A couple of small things of note here... firstly, we'll be using the
:before
and
:after
pseudo-elements a lot for checking out variable values, and including text in some places (either because it would bulk up the signature, or because it depends on the time, etc.). You'll probably want to be a little familiar with the workings of those elements for later. Secondly, you'll notice we don't just write
content: @time;
, but instead have to "add" the empty string
''
. This is simply because the
@time
variable is a number, and we have to force it into a string to use it as a
content
value.
Since the CSS is going to get bulky, too, we can quickly push the extra stuff into a skin, and just import it, as well! Of course, the
@time
variable still needs to be left inside the BBCode, or else the tags won't actually do anything... Regardless, if we were to import a skin called
abcd
from my user (whose user code is
jZ
), we'd get:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; [/style] [sc=content][/sc]
Skin CSS
.content:before { content: '' + @time; }
Output
Now we can get into the cool stuff! We've got our local time, let's make a little clock! To begin with, here's some CSS to make a clock that doesn't move:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; [/style] [sc=clock][/sc]
Skin CSS
@clock-size=50px; @hand-width=4px; .clock { border-radius: 50%; width: @clock-size; height: @clock-size; position: relative; overflow: hidden; border: 2px solid currentcolor; &:before, &:after { content: ''; width: @hand-width; position: absolute; bottom: ~'calc(50% - @{hand-width}/2)'; left: ~'calc(50% - @{hand-width}/2)'; transform-origin: @hand-width/2 ~'calc(100% - @{hand-width}/2)'; background-color: currentcolor; border-radius: @hand-width/2; } &:before { height: @clock-size/2; transform: rotate(90deg); } &:after { height: @clock-size/4; transform: rotate(30deg); } }
Output
Okay! So we have a clock, and we just need to add in the rotating hands! A little bit of maths then gives us:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; [/style] [sc=clock][/sc]
Skin CSS
@clock-size=50px; @hand-width=4px; @rotation-hr=6deg * (@time / 12); @rotation-min=6deg * @time; .clock { border-radius: 50%; width: @clock-size; height: @clock-size; position: relative; overflow: hidden; border: 2px solid currentcolor; &:before, &:after { content: ''; width: @hand-width; position: absolute; bottom: ~'calc(50% - @{hand-width}/2)'; left: ~'calc(50% - @{hand-width}/2)'; transform-origin: @hand-width/2 ~'calc(100% - @{hand-width}/2)'; background-color: currentcolor; border-radius: @hand-width/2; } &:before { height: @clock-size/2; transform: rotate(@rotation-min); } &:after { height: @clock-size/4; transform: rotate(@rotation-hr); } }
Output
And there we have it! We can successfully use the current time (locally) to make a clock tick! Now, that alone is nice, but we can use this time for plenty of stuff, though you need to jump through some hoops first... Let's take a look!
To begin working with the local time in earnest, we need to be able to exract what we want from the value we're given. If we have the number of minutes past midnight, we might want to break that into hours and minutes separately, without needing to set two variables (which doubles the amount of BBCode in our signature's
[style]
tag). Towards this end, we can try to use the
mod()
function from LESS. If you're not familiar with modular arithmetic, I encourage you to take a look at the Wikipedia article.
So... let's try and extract the hour and minute, by taking the time provided modulo 60, and a little bit of basic maths to get the hour:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; [/style] It is [sc=hr][/sc]:[sc=min][/sc].
Skin CSS
@min=mod(@time, 60); @hr=(@time - @min) / 60; .hr, .min { display: inline; } .hr:before { content: @hr + ''; } .min:before { content: @min + ''; }
Output
CSS style error: expecting number: failed at `content: @hr + '';`
... what? That's weird... it says it's expecting a number, but
@hr
is a number? What about if we try just checking the minute?
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; [/style] It is [sc=hr][/sc]:[sc=min][/sc].
Skin CSS
@min=mod(@time, 60); //@hr=(@time - @min) / 60; .hr, .min { display: inline; } //.hr:before { // content: @hr + ''; //} .min:before { content: @min + ''; }
Output
CSS style error: expecting number: failed at `content: @min + '';`
Nope... So there's something wrong with our
mod()
calculation. This was... annoying to figure out, but there is thankfully a way around this, which I call variable deference.
When you save a skin or try to post/preview some BBCode, PFQ tries to check everything makes sense before it lets you. Doing this when
mod()
is involved kind of confuses it. So, we kind of side-step the issue by splitting the variable across two contexts: the definition will be inside the skin, and we'll link it to another variable inside the BBCode. In this way, the skin and preview will pass the verification check and save, while we can still use the result of our modulus inside the skin:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; @in-a=@out-a; [/style] It is [sc=hr][/sc]:[sc=min][/sc].
Skin CSS
@out-a=mod(@time, 60); @min=@in-a; @hr=(@time - @min) / 60; .hr, .min { display: inline; } .hr:before { content: @hr + ''; } .min:before { content: @min + ''; }
Output
It is
:
.
Well... That's certainly some progress, but what's with the
Array
? Turns out
mod()
is kind of... broken. For some reason, it will always append
Array
to its result. Once again, something that took a bit of trial-and-error to get around, but there's another function that can help us here:
unit()
! The
unit()
function is used to replace the units of something, so
unit(4px, em)
would replace the
px
with
em
. In our case, it identifies
Array
as a unit, so we can replace it with something else, or even just remove it completely:
BBCode
[style] @time=[math][datetime=G*60+i][math]([datetime=U]Australia/Sydney[/datetime]-[datetime=U] [/datetime])/3600[/math][/datetime][/math]; @import 'skins/user/j/Z/abcd/__extra'; @in-a=@out-a; [/style] It is [sc=hr][/sc]:[sc=min][/sc].
Skin CSS
@out-a=unit(mod(@time, 60)); @min=@in-a; @hr=(@time - @min) / 60; .hr, .min { display: inline; } .hr:before { content: @hr + ''; } .min:before { content: @min + ''; }
Output
It is
:
.
There we go! Much better. Now, we have a way to use the number we pass in for more! There's also other functions, that are less finicky:
floor()
and
ceil()
, for example. Unfortunately a lot of useful LESS features aren't included in PFQ's engine, but there's generally ways around them.
A quick note: since LESS does maths for you, you don't actually need the
[math]
tag around the
@time
definition; LESS will do it for you! If you're hoping to decrease the code inside your signature, you'll probably want to remove all the new lines, and reduce
@time
to something like
@a
to save characters.
And finally, if you do some more complicated things you may end up deferring a bunch of variables, which can bulk up your signature a bit. To avoid this, you can actually
@import
your signature's skin CSS into another skin, and keep all the deferred variables in there instead! To do this, you need to get around the skin CSS validator though, which still requires a sneaky trick. As a last example to leave you with, this is how you can double import with deferred variables:
BBCode
[style] @import 'skins/user/j/Z/skin1/__extra'; @-='s'; [/style] ...
skin1
CSS
@import @- + 'kins/user/j/Z/skin2/__extra'; // Deferred variables here
In this way, we only need to keep one variable inside the BBCode (well, other than those relying on other BBCode), and the rest of the definitions can be elsewhere!
Stay hydrated~ Avatar by Squidacious [p]

Cannot post: Please log in to post

© PokéFarm 2009-2024 (Full details)Contact | Rules | Privacy | Reviews 4.6★Get shortlink for this page