Friday, 2 May 2025
Affinity-for-mermaid
Thursday, 24 June 2021
How do I find all the HTML elements that aren't getting my preferred font?
A quickie script that saved a lot of manually combing through the DOM
Someone noticed that certain elements in our React app were not getting the desired font-face, instead getting plain-old Arial. I wanted to be able to programmatically sniff them out in the browser, so here's what I came up with, mainly thanks to an answer on Stack Overflow for finding all elements on a page, and the MDN documentation for the getComputedStyle function which browsers helpfully expose.
Whack this in your browser's Javascript console and you should be able to hover on any element that is listed to see it on the page:
// Start where React attaches to the DOM const reactRoot = document.getElementById("root"); // Get all elements below that const kids = reactRoot.getElementsByTagName("*"); for (var e of kids) { if (window.getComputedStyle(e)["font-family"] === "Arial") { console.log(e); // Allows it to be hovered in console } }
In case you were wondering, the culprit here was a button that didn't have its font-family set - and Chrome (perhaps others) will use its default (user-agent stylesheet) font style for that in preference to what you have set on the body, which you might be forgiven for assuming gets cascaded down.
Friday, 24 August 2018
Building a retro game with React.js. Part 5 - Fill 'er up
The answer was to challenge my own comfort zone. Allow myself to quote, myself:
I could do that with an HTML canvas, but I'm (possibly/probably wrongly) not going to use a canvas. I'm drawing divs dammit!
After all, if ever there was a time and place to learn how to do something completely new, isn't it a personal "fun" project? After a quick scan around the canvas-in-React landscape I settled upon React-konva, which has turned out to be completely awesome for my needs, as evidenced by the amount of code changes actually needed to go from my div-based way of drawing a line to the Konva way:
The old way:
const Line = styled('div')` position: absolute; border: 1px solid ${FrenzyColors.CYAN}; box-sizing: border-box; padding: 0; `; renderLine = (line, lineIndex) => { const [top, bottom] = minMax(line[2], line[4]); const [left, right] = minMax(line[1], line[3]); const height = (bottom - top) + 1; const width = (right - left) + 1; return <Line key={`line-${lineIndex}`} style={{ top: `${top}px`, left: `${left}px`, width: `${width}px`, height: `${height}px` }} />; }
The new way:
import { Line } from 'react-konva'; renderLine = (line, lineIndex) => { return ( <Line key={`line-${lineIndex}`} points={line.slice(1)} stroke={FrenzyColors.CYAN} strokeWidth={2} /> ); }
... and the jewel in the crown? Here's how simple it is to draw a closed polygon with the correct fill colour; by total fluke, my "model" for lines and polygons is virtually a one-to-one match with the Konva Line object, making this amazingly straightforward:
renderFilledArea = (filledPolygon) => { const { polygonLines, color } = filledPolygon; const flatPointsList = polygonLines.reduce((acc, l) => { const firstPair = l.slice(1, 3); return acc.concat(firstPair); }, []); return ( <Line points={flatPointsList} stroke={FrenzyColors.CYAN} strokeWidth={2} closed fill={color} /> ); }
Time Tracking
Probably at around 20 hours of work now.Monday, 16 July 2018
Building a retro game with React.js. Part 4 - Drawing the line somewhere
Bugs Galore
It was at this point where I started being plagued by off-by-one errors; it seemed everywhere I turned I was encountering little one-pixel gaps when drawing lines, because:- My on-screen lines are actually 2px wide
- My line-drawing function was doing an incorrect length calculation (had to do (right - left) + 1)
- I was not updating my position at the right time, so was storing my "old" position as the current line's end point; and;
- I was naively using setState and expecting the new this.state to be visible immediately
My solution to almost all of these problems (with the exception of the UI line-drawing function) was to write a heap of unit tests; these generally flushed things out pretty quickly.
Writing the line-drawing function was a weird experience. Virtually every software development "environment" I've ever used before, from BBC Basic on my Acorn Electron on, has had a function like drawLine(startX, startY, endX, endY);. And I could do that with an HTML canvas, but I'm (possibly/probably wrongly) not going to use a canvas. I'm drawing divs dammit! Here's what my function looks like:
renderLine = (line, lineIndex) => { const [top, bottom] = minMax(line[2], line[4]); const [left, right] = minMax(line[1], line[3]); const height = (bottom - top) + 1; const width = (right - left) + 1; return <Line key={`line-${lineIndex}`} style={{ top: `${top}px`, left: `${left}px`, width: `${width}px`, height: `${height}px` }} />; }Where minMax is a simple function that returns [min, max] of its inputs, and Line is a React-Emotion styled div:
const Line = styled('div')` position: absolute; border: 1px solid ${FrenzyColors.CYAN}; box-sizing: border-box; padding: 0; `;Notice that I resisted the temptation to pass the top, left etc into the Line wrapper. The reason for this is that doing so results in a whole new CSS class being created, and getting applied to the line, every time one of these computed values changes. This seems wasteful when most of the line's attributes remain the same! So I use an inline style to position the very-thin divs where I need it.
Time Tracking
Up to about 12 hours by my rough estimate.Friday, 15 June 2018
Building a retro game with React.js. Part 3 - I Like To Move It
Again, starting with the easy stuff, I wanted the four directional keys to move the Player around. But in Frenzy, you can only move (as opposed to draw) along the boundaries of the game area and on lines you have already drawn. So if we look at my first iteration of the code in GameArea to handle a request to move the Player left, it's something like this:
update = () => { if (this.keyListener.isDown(this.keyListener.LEFT)) { this.moveLeft(); } }; moveLeft = () => { if (this.canMove(Direction.LEFT)) { this.setState({ playerX : this.state.playerX -1 }); } }I ended up bundling quite a lot of smarts into the Direction enumeration in order to make the logic less "iffy" and more declarative. That one Direction.LEFT key encapsulates everything that is needed to check whether a) the player is on a line that has the correct orientation (horizontal) and b) there is room on that line to go further to the left.
A line looks like this:
[Orientation.HORIZONTAL, 0, 0, 478, 0], // startX, startY, endX, endYand Direction looks like this:
export const Direction = { LEFT: { orientation: Orientation.HORIZONTAL, primaryCoord: (x, y) => y, lineToPrimaryCoord: (line) => line[2], secondaryCoord: (x, y) => x, testSecondary: (c, line) => c > Math.min(line[1], line[3]) }, ... }
My test for whether I can move in a certain direction is:
static canPlayerMoveOnExistingLine = (playerX, playerY, direction, lines) => { const candidates = lines.filter(line => { return (line[0] === direction.orientation) }); const pri = direction.primaryCoord(playerX, playerY); const primaryLines = candidates.filter(candidateLine => { return direction.lineToPrimaryCoord(candidateLine) === pri; }); if (primaryLines.length > 0) { const sec = direction.secondaryCoord(playerX, playerY); const found = primaryLines.find(line => { return direction.testSecondary(sec, line); }); return typeof found !== 'undefined'; } return false; }Declared static for ease of testing - easy and well worth doing for something like this where actually moving the player around is time-consuming and tedious. It's working well as it stands, although as we all know, naming things is hard. It's pretty easy to follow the process though. At this point I'm holding a lines array in this.state and doing filter and find operations on it as you can see above. We'll have to wait and see whether that will be fast enough. It may well be a good idea to keep a currentLine in state, as most of the time it will be unchanged from the last player movement. Next up, it's time to start drawing some new lines on the screen!
Kudos
I am starting to build up some tremendous respect for the original author of this game; although often dismissed as "very simple" there are some tricky little elements to coding this game and I'm only just scratching the surface. To achieve the necessary performance on an 8-bit, 1MHz processor with RAM measured in the handfuls of kilobytes is super impressive. Assembly language would have been necessary for speed, making the development and debugging a real pain. I haven't even started thinking about how to do the "fill" operation once a line has been drawn and it encloses some arbitrary space, but I suspect the original developer "sniffed" the graphics buffer to see what was at each pixel location - a "luxury" I don't think I'll have!Time Tracking
Up to about 6 hours now.Monday, 11 June 2018
Building a retro game with React.js. Part 2 - Screens
One thing I hadn't thought about was the behaviour of listening to the P key to pause and also resume. The first iteration of the code would flicker madly between the game page and the paused page whenever the P key was pressed - the game loop runs so fast that the app would transition multiple times between the two pages, because the isDown test would simply toggle the state of this.state.paused. I messed around for a while with storing the UNIX time when the key was first pressed, and comparing it to the current time, before realising I really just needed to know what "direction" was being requested, and then waiting for this "request" to finish before completing the transition.
... debounce = (testCondition, key, newState) => { if (testCondition) { if (this.keyListener.isDown(key)) { // No. They are still holding it return true; } this.setState(newState); } return false; }; handleKeysWhilePaused = () => { if (this.debounce(this.state.pauseRequested, P, { pauseRequested: false, paused: true })) { return; } if (this.debounce(this.state.unpauseRequested,P, { unpauseRequested: false, paused: false })) { return; } if (this.keyListener.isDown(Q)) { this.props.onGameExit(); } if (this.keyListener.isDown(P)) { this.setState({unpauseRequested: true}); } } handleGameplayKeys = () => { if (this.keyListener.isDown(P)) { this.setState({pauseRequested: true}); } } update = () => { if (this.state.pauseRequested || this.state.paused) { this.handleKeysWhilePaused(); } else { this.handleGameplayKeys(); } }; ...Effectively I've implemented an isUp(), which is surprisingly not in the react-game-kit library. Oh well, it's all good learning.
Time tracking
I'd estimate the total time spent on the game so far would be 3-4 hours.Friday, 8 June 2018
A New Old Thing; Building a retro game with React.js. Part 1 - Background and Setup
At the risk of being immodest, I'm comfortable with those concepts - Design Patterns from waaaay back and the functional paradigms from my five-year (and counting) love affair with Scala. What I wanted to explore was - what would happen if I built a React app by myself, endeavouring to write the cleanest, purest software based upon the best starting-point we currently have? How productive could I be? How long would it take to build a working full app? How would maintenance go? How quickly could I add a new feature?
As my day job is basically building CRUD apps, I wanted to do something a lot more fun for this side-project. And what could be more fun than a game? (Mental note: ask people working at Electronic Arts...) There's also a pleasing circularity in building a game and documenting how I did it - back in my earliest days of having a computer, aged about 7, I would buy magazines with program listings and laboriously enter them, line-by-line, while marvelling at how anyone could really have been good enough to know how to do this.
The Game
I'll admit, I've never built a non-trivial game before, but I think attempting an 8-bit home computer game I remember fondly from my childhood, on top of 2018's best front-end technologies, should be about the right level of difficulty.The game I'll be replicating is called Frenzy; a Micro Power publication for the BBC B, Acorn Electron and Commodore 64. My machine was the Electron - basically a low-cost little brother to the mighty Beeb; highly limited in RAM and CPU, titles for this platform usually needed substantial trimming from their BBC B donor games, despite using the same BBC BASIC language.
Check out the links above for more details and screenshots, but the game is basically a simplified version of "Qix" or "Kix" where the object is to fill in areas of the screen without being hit by one or more moving enemies.
Just for the hell of it, I'm going to place this game front-and-centre on my homepage at http://www.themillhousegroup.com, which I just nuked for this purpose. The page is now a React app being served off a Play Scala backend as per my new-era architecture, and the key technologies I'm using so far are:
- React via Create-React-App
- react-game-kit for game secret-sauce
- react-emotion for component styling
Initial Development
To develop the game, I decided to start from the start. The welcome page would need to be suitably old-skool but would force me to consider a few things:- What screen size should I be working to?
- Can I get a suitably chunky, monospaced font?
- Press Space to start sounds easy, but how do I make that actually work?
Decisions
The original Frenzy must have operated in the BBC's graphical MODE 1 because it used a whopping 4 colours and the pixels were square. So that means the native resolution was 320x256. While it would be tempting to stick to that screen size and thus have it fit on every smartphone screen, I've decided to double things and target a 640x512 effective canvas.Some searching for 8-bit fonts led me to "Press Start 2P" which, while intended to honour Namco arcade machines, is near enough to the chunky fonts I remember fondly from my childhood home computer that I can't go past it:
As a tiny nod to the present, the "screen" is actually slightly transparent and has a drop shadow - showing how far we've come in 34 years!
The final piece of the welcome screen was achieved by mounting the FrenzyGame component in a React-Game-Kit Loop and using the KeyListener to subscribe to the keys I care about - a quick perusal of the demo game showed me how to use it:
class FrenzyGame extends Component { constructor(props) { super(props); this.keyListener = new KeyListener(); this.state = { gameOver: true }; } componentDidMount() { this.loopSubscriptionId = this.context.loop.subscribe(this.update); this.keyListener.subscribe([ this.keyListener.SPACE ]); } componentWillUnmount() { this.context.loop.unsubscribe(this.loopSubscriptionId); this.keyListener.unsubscribe(); } update() { if (this.state.gameOver) { if (this.keyListener.isDown(this.keyListener.SPACE)) { this.setState({ gameOver: false }); } } }; ... render() { return this.state.gameOver ? this.renderWelcomeScreen() : this.renderGame(); } }
Sunday, 29 October 2017
Stack Evolution part 2
Javascript | JQuery, Moment.js, etc | |
"Presentation" | JSON/AJAX | HTML/LESS |
Controllers | Play (Reactive, Async) | |
Services | Play - RESTful API calls | Mondrian |
Persistence | MongoDB (via ReactiveMongo) |
I am simply delighted with the performance, scalability, maintainability and reliability of the entire stack from the Controllers layer down - i.e. Scala, Play and Mongo. (Incidentally, I've been running these apps on Heroku with MongoDB provided by MLab, and they have been similarly excellent). So that will not be changing any time soon.
What is no longer tenable is the mixture of HTML (including form submissions), LESS and per-page Javascript. At the top of the table, (i.e. front-end technologies), there is just too much awesomeness happening in this space to ignore. To me, React.js is the current culmination of the best thinking in the front-end world. The way every concept is the most reduced-down thing that could work (as opposed to the competition's kitchen-sink approach) really makes it a pleasure to learn and use.
Currently I'm absolutely loving Create-React-App as a brilliant bootstrapper that continues to add value even once you're up and running. It's got finely-honed and sensible defaults for things like Webpack, is upgradeable in-place, is beautifully documented and is almost psychic in always offering good suggestions or output as to what it's just done, or what can be done next. I currently have no plans to "eject" Create-React-App from any of the front-end projects I'm working on - it's just too useful to keep around.
Into this mix I've also added React Cosmos - this is a "component showcase" system that allows a super-rapid way to see all of the possible "states" of a given React component. The React props that a component needs are specified in fixture files, and Cosmos supplies a nice web UI to browse around and check that changes made to a component are working well and looking good in all of its potential states. It works excellently with the hot-reloading facilities of Create-React-App and really helps nail down component interfaces.
Another element I'm using to try and keep front-end complexity in check is Styled Components. Go have a read of their Github page for the full run-down but basically, I can get the best of both worlds with global CSS used where appropriate, keeping it DRY, together with individual components that won't mess with each other. It also massively helps in stopping the "mental CSS selector" problems during refactoring as observed by Ryan Florence. Extremely cool.
So to summarise, here's my 2017-and-beyond software stack:
Javascript | React.js (with Cosmos) | |
"Presentation" | JSON/AJAX | JSX/CSS/Styled Components |
Controllers | Play (Reactive, Async) | |
Services | Play - RESTful API calls | Mondrian |
Persistence | MongoDB (via ReactiveMongo) |
Friday, 29 September 2017
Stack Evolution part 1
The "revenue-earning" configuration I've rolled out time and time again looks almost-unfailingly like this:
Javascript | JQuery, Moment.js, etc | |
"Presentation" | JSON/AJAX | HTML/LESS |
Controllers | Play (Reactive, Async) | |
Services | Play - RESTful API calls | Mondrian |
Persistence | MongoDB (via ReactiveMongo) |
Yep. Decidedly unsexy, and yes, occasionally the javascript can get a bit funky and coupled to the HTML, but it works and with careful attention to the principles of Single Responsibility and Least Surprise, a "vertical slice" through the functionality would look like:
Javascript | /assets/js/author.js | |
"Presentation" | /views/html/author/author.scala.html /assets/css/author.less |
|
Controllers | /controllers/AuthorController.scala | |
Services | /models/Author.scala /services/AuthorService.scala |
|
Persistence | [Authors collection in MongoDB] |
... where no source file is more than 200 lines of code. Nothing too controversial there, I think you'd agree.
However...
My exposure over the last 18 months to React.js has truly opened my eyes to the potential of a true front-end application (as opposed to the very 2010-era progressively-enhanced-markup approach I've described above). In the next post I'll show the architecture I've been calling the CRAP-stack which has been making working on the Javascript front-end as pleasant as doing the heavy-lifting with Scala in the back-end (i.e. very!)Friday, 31 March 2017
IE9/10: Silent failure to POST a form
After checking and fixing the minor HTML validity issues, replacing the submit button with an input type="submit" and various other insignificant tweaks, I actually read the error message IE was showing me:
I'd been obsessing over the way the request was being aborted so fast and was thinking there was some kind of weird cross-origin thing blocking my POST (my page is first step in a payment process - summarising the costs, and clicking the big Pay Now button caused a POST to a third-party payment processor). I'd completely failed to notice the final bullet point in the "More Information" section which mentions SSL and TLS versions. After heading into Settings -> Internet Options -> Advanced -> Security and checking "Use TLS 1.2", everything started working perfectly.
I have no idea why Microsoft would implement new versions of an important protocol but then default the support to OFF. Must be one of the backwards-compatibility hacks that (apparently) they could then finally be rid of in IE 11, where it is turned ON by default.
Hope it helps someone.
Thursday, 18 August 2016
Deep-diving into the Google pb= embedded map format
Let's take a look at a Google "Embed map" URL for a random lat-long. You can obtain one of these by clicking a random point on a Google Map, then clicking the lat-long hyperlink on the popup that appears at the bottom of the page. From there the map sidebar swings out; choose Share -> Embed map - that's your URL.
"https://www.google.com/maps/embed?pb= !1m18!1m12!1m3!1d3152.8774048836685 !2d145.01352231578036 !3d-37.792912740624445!2m3!1f0!2f0!3f0!3m2!1i1024!2i768 !4f13.1!3m3!1m2!1s0x0%3A0x0!2zMzfCsDQ3JzM0LjUiUyAxNDXCsDAwJzU2LjYiRQ !5e0!3m2!1sen!2sau!4v1471218824160"Well, it's not pretty, but with the help of Andrew Whitby's cheat sheet and the comments from others, it turns out we can actually render it as a nested structure knowing that the format [id]m[n] means a structure (multi-field perhaps?) with n children in total - my IDE helped a lot here with indentation:
"https://www.google.com/maps/embed?pb=" + "!1m18" + "!1m12" + "!1m3" + "!1d3152.8774048836685" + "!2d145.01352231578036" + "!3d-37.792912740624445" + "!2m3" + "!1f0" + "!2f0" + "!3f0" + "!3m2" + "!1i1024" + "!2i768" + "!4f13.1" + "!3m3" + "!1m2" + "!1s0x0%3A0x0" + "!2zMzfCsDQ3JzM0LjUiUyAxNDXCsDAwJzU2LjYiRQ" + "!5e0" + "!3m2" + "!1sen" + "!2sau" + "!4v1471218824160"It all (kinda) makes sense! You can see how a decoder could quite easily be able to count ! characters to decide that a bang-group (or could we call it an m-group?) has finished. I'm going to take a stab and say the e represents an enumerated type too - given that !5e0 is "roadmap" (default) mode and !5e1 forces "satellite" mode.
So this is all very well but it doesn't explain why URLs that I generate using the standard method don't actually put the lat-long I selected into the URL - yet they render perfectly! What do I mean? Well, the lat-long that I clicked on (i.e. the marker) for this example is actually:
-37.792916, 145.015722And yet in the URL it appears (kinda) as:
-37.792912, 145.013522Which is enough to be slightly, visibly, annoyingly, wrong if you're trying to use it as-is by parsing the URL. What I thought I needed to understand now was this section of the URL:
"!1d3152.8774048836685" + "!2d145.01352231578036" + "!3d-37.792912740624445" +Being the "scale" and centre points of the map. Then I realised - it's quite subtle, but for (one presumes) aesthetic appeal, Google doesn't put the map marker in the dead-centre of the map. So these co-ordinates are just the map centre. The marker itself is defined elsewhere. And there's only one place left. The mysterious z field:
!2zMzfCsDQ3JzM0LjUiUyAxNDXCsDAwJzU2LjYiRQSure enough, substituting the z-field from Mr. Whitby's example completely relocates the map to put the marker in the middle of Iowa. So now; how to decode this? Well, on a hunch I tried base64decode-ing it, and bingo:
% echo MzfCsDQ3JzM0LjUiUyAxNDXCsDAwJzU2LjYiRQ | base64 --decode 37°47'34.5"S 145°00'56.6"ESo there we have it. I can finally parse out the lat-long of the marker when given an embed URL. Hope it helps someone else out there...
Wednesday, 29 June 2016
ReactJS - Early Thoughts
I have always been comfortable with HTML, and first applied a CSS rule (inline) waaay back in the Netscape 3.0 days:
<a href="..." style="text-decoration: none;">Link</a>I'm pretty sure there were HTML frames involved there too. Good times. Anyway, the Javascript world had always been a little too wild-and-crazy for me to get deeply into; browser support sucked, the "standard library" kinda sucked, and people had holy wars about how to structure even the simplest code.
Up to about 2015, I'd been happy enough with the basic tools - JQuery for DOM-smashing and AJAX, Underscore/Lodash for collection-manipulation, and bringing in Bootstrap's JS library for a little extra polish. The whole thing based on full HTML being rendered from a traditional multi-tiered server, ideally written in Scala. I got a lot done this way.
I had a couple of brushes with Angular (1.x) along the way and didn't really see the point; the Angular code was always still layered on top of "perfectly-good" HTML from the server. It was certainly better-structured than the usual JQuery mess but the hundreds of extra kilobytes to be downloaded just didn't seem to justify themselves.
Now this year, I've been working with a Real™ Front End project - that is, one that stands alone and consumes JSON from a back-end. This is using Webpack, Babel, ES6, ReactJS and Redux as its principal technologies. After 6 weeks of working on this, here are some of my first thoughts:
- Good ES6 goes a long way to making Javascript feel grown-up
- Bad The whole Webpack-Babel-bundling thing feels really rough - so much configuration, so hard to follow
- Good React-Hot-Reloading is super, super-nice. Automatic browser reloads that keep state are truly magic
- Bad You can still completely, silently toast a React app with a misplaced comma ...
- Good ... but ESLint will probably tell you where you messed up
- Bad It's still Javascript, I miss strong typing ...
- Good ... but React PropTypes can partly help to ensure arguments are there and (roughly) the right type
- Good Redux is a really neat way of compartmentalising state and state transitions - super-important in front-ends
- Good The React Way of flowing props down through components really helps with code structure
So yep, there were more Goods than Bads - I'm liking React, and I'm finally feeling like large apps can be built with JavaScript, get that complete separation between back- and front-ends, and be maintainable and practical for multiple people to work on concurrently. Stay tuned for more!
Wednesday, 8 October 2014
Walking away from Run@Cloud. Part 1: Finding A Worthy Successor
I was using a number of CloudBees services, namely:
- Dev@Cloud Repos - Git repositories
- Dev@Cloud Builds - Jenkins in the cloud
- Run@Cloud Apps - PaaS for hosted Java/Scala apps
- Run@Cloud Database Service - for some MySQL instances
- MongoHQ/Compose.io ecosystem service - MongoDB in the cloud
In short, a fair bit of stuff: ... the best bit being of course, that it was all free. So now I'm hunting for a new place for most, if not all, of this stuff to live, and run, for $0.00 or as close to it as conceivably possible. And before you mention it, I've done the build-it-yourself, host-it-yourself thing enough times to know that I do not ever want to do it again. It's most definitely not free for a start.
After a rather disappointing and fruitless tour around the block, it seemed there was only one solution that encompassed what I consider to be a true "run in the cloud" offering, for JVM-based apps, at zero cost. Heroku.
Tuesday, 27 September 2011
Spring Roo Experiment, part 2
Making it perty
Roo does a great job of making a functional CRUD website, and that's great for an admin page. But public-facing websites, especially ones developed in 2011, need to look funky.
Enter Bootstrap, from the good people at Twitter. I'm not a Tweeter, but I have to extend maximum love and respect to these guys for turning out something to make things look so good, so easily.
Including the Bootstrap CSS in your webapp is like having the most web-2.0, rounded-cornered, missing-vowelled, fixie-riding front-end guy pairing with you, except he's actually completely finished the work, it works on all browsers and you don't have to listen to him talk about his iPhone all day :-)
Seriously though, Bootstrap does exactly what it says on the tin, with beautifully-clean, semantic markup, a wonderfully easy-to-use grid system and a contemporary, stylishly-minimal look.
I downloaded the Bootstrap CSS file and added it to my Spring Roo project by following these instructions - so now I can flip between the standard green Roo theme for my admin pages, and the super-funky Bootstrap theme for public pages. Lovely.
Tuesday, 30 August 2011
I <3 WWW
As I'm not an iOS or Android developer I attended the August "Mobile Focused" Melbourne YOW! Night with a certain degree of reluctance.
I had visions of skinny-jeaned, black-plastic-spectacled iOS-developing hipsters telling me how, like, awesome their, like, app was because like, it totally like mashed up the tweeting paradigm by like ironically cross-pollenating the artisan tumblr mixtape vibe (thanks Hipster Ipsum!).
And while there was a certain amount of that, I was somewhat refreshed to hear from a couple of speakers (native mobile app developers, mind you) that a great deal of the time, when you strip away all of the hype, what the customer needs can be addressed with a mobile-focused website.
I've seen first-hand the hype-storm that (in particular) the iPad has created. One of the speakers at YOW! painted a pretty accurate picture:
- CEO's wife buys CEO a new iPad because they're shiny
- CEO can't really use iPad but notes these apps on the home screen have pretty icons
- CEO sees other companies have apps
- CEO decides his company needs an app
- CEO doesn't exactly know what app is for, but knows that it's needed NOW
What follows (MyCompanyApp v1.0) is usually an app-ification of what the company's current website offers. It is a pointless, money-wasting exercise that gives the CEO his app icon that he can show off in the golf club, but actually has zero advantage over his website (actually negative advantage because it doesn't self-update like a web page).
If the iPad had a way to add a shortcut to a webpage as a home-screen icon, the whole thing could have been done in 30 seconds.
As mobile devices get increasingly capable, well-written, lightweight web pages with advanced features like HTML5 Geolocation can get pretty damn close to the "native app experience" - with none of the approval-process or old-installed-version hassles.
So that's the basket where I'm putting my eggs for the next little while; I just hope the egg doesn't end up on my face :-)
Monday, 22 August 2011
The CSS F(l)ail
I've written before about CSS and why it seems to be so hard for a Typical Java Developer (TJD) to craft "nice" HTML+CSS presentation layers.
Here's the behavioural pattern I've observed when a TJD has to do some front-end web-work
:- Panic - rather than embrace the opportunity to learn a new paradigm/technology, as they would probably enjoy doing if it was a Java API
- Copy-and-paste from the existing codebase - despite knowing that this leads to unmaintainable code no matter what the language
- Copy-and-paste from the web - without understanding what is going on
- Randomly tweak values in the browser using Firebug or Chrome Developer Tools until it "looks right" (I've started to call this CSS flailing)
- Give Up And Use Tables (for non-tabular data). The ultimate fail
In posts-to-come I'm going to try and identify some CSS "patterns" that a software developer can actually understand (as opposed to copy-and-pasting) which hopefully will put an end to the above behaviours, any of which would be regarded as unforgivable if it was perpetrated in the squeaky-clean corridors of Java-land ;-) but are somehow considered par-for-the-course in the unfamiliar yet forgiving world of UI...
Tuesday, 19 April 2011
The Joy of CSS
Why It's hard to be a CSS Rockstar
Writing CSS is hard. Writing good CSS is really hard.
I've been using CSS in anger for about 4 years now, and I'd rate my skills at no more than 5 out of 10. I'm the first to admit I spend a lot of time Googling when I'm tweaking CSS, and I'm sure I'm not the only one. I've only ever come across one Java-world developer who could craft elegant, cross-browser CSS solutions with appropriate use of semantic HTML, and he was an exceptional front-end guy who could make PhotoShop walk around on its front paws, and write clean, performant JavaScript while balancing a ball on his head. Seriously.
So why do otherwise-competent software developers find it so hard to produce good CSS?
- width: doesn't really mean width: The W3C's box model might be intuitive to some, but ask a typical software developer to draw a box with width: 100px; border: 5px; and I can virtually guarantee that the absolute width of the result will be 100 pixels while the internal (or "content" in W3C-speak) width will be 90 pixels. Given this, it becomes slightly easier to forgive Microsoft for their broken box model in IE5
- Inconsistent inheritance: As OO developers, we've come to expect every property of an object to be inherited by its children. This is not the case in CSS, which can lead to a non-DRY sensation that is uncomfortable
- It's a big API: Although there is a lot of repetition (e.g.: border; border-top; border-top-width; border-top-color; border-left; border-left-style; etc etc etc) there are also tons of tricky shortcuts which behave dramatically differently depending on the number of "arguments" used. Compare border-width: thin thick;
to border-width: thin thin thick;
to border-width: thin thin thin thick; - You can't debug CSS Selectors: The first move of most developers when they have to change some existing styling is to whack a background-color: red; into the selector they think should be "the one". And then have to hunt around a bit more when their target div doesn't turn red ...
- Semantic, understandable and succinct?!?!: Most developers understand that using CSS classes with names like boldface is not cool, and nor is using identifiers called tabbedNavigationMenuElementLevelTwo - but getting the damn thing even working is hard enough without having to wonder if the Gods of HTML would sneer at your markup...
Tuesday, 5 April 2011
Whoooosh! Part 2
Making it happen
YSlow gave this site a solid 'B' for performance, but I knew it could be better.
The main culprit was the lack of expiry headers on resources, meaning the browser had to reload them each page visit. Dumb. I was shocked to find that Tomcat had no easy way to get this going, but this has now been rectified in Tomcat 7.
Next up was optimising my images. YSlow can helpfully slurp your site through Yahoo's smush.it service which saved me a good 25% in download cruft. Win.
"Sprited" CSS images were something I knew I wanted to do, but had yet to find a nice tool to make it happen. Now that SmartSprites is a reality, I've been using it to both optimise the images and generate the CSS that controls them. I'm also planning on using JAWR (which utilises SmartSprites) as part of my standard toolchain for Java web development, pushing out minified, optimised, spritified HTML/CSS/JS content whenever the build target is not "dev".
Things were going well, and I was up to 'A' Grade, but my "favicon" was still being re-downloaded each-and-every page visit. I had to tell Tomcat that it was an image, and then the Expires Filter could deal with it as per all the others:
(in conf/web.xml):
<mime-mapping>
<extension>ico</extension>
<mime-type>image/x-icon</mime-type>
</mime-mapping>
The final step was getting in under Pebble's covers and tweaking the page template, such that:
- Javascript sits at the bottom of the page; and
- Practically-useless files (like an empty print.css) are not mentioned
And with that, I'm currently scoring 95%. I'm losing the 5% for not gzipping my content (which will probably have been remedied by the time you read this), and having too many separate JavaScript files (a fact I need to investigate further - where is this blog even using JavaScript?) - but I'm happy; my little corner of the web is a snappier place.