Hi, I'm James.
I make websites.
Iʼm currently leading the talented dev team at Superrb where I build awesome websites and interactive experiences like these
Turns out, the big names have
.My work has been recognised by some of the industryʼs top players.Awwwards
- Honorable Mention
Awwwards
- Honorable Mention
- Nominated for 2022 E-commerce Site of the Year
Typewolf
- Site of the Day
Awwwards
- Site of the Day
- Developer Award
- Nominated for 2019 Developer Site of the Year
Creative Pool
- Nominated for 2019 Annual Web Shortlist
CSS Design Awards
- Site of the Day
- Best Innovation
- Best UI Design
- Best UX Design
Some things are
to keep to myselfI build open source tools, because good ideas are better when shared./** * LiveNodeList * * An alternative to document.querySelector * that monitors the DOM for changes */
import { LiveNodeList } from 'live-node-list'
const items = new LiveNodeList('.my-selector')
// Attach an event listener to each item
// in the node list. The listener will automatically
// be attached to any new elements matching the
// selector when they are added to the DOM
items.addEventListener('click', (e) => {
alert(`Clicked! ${e.target.id}`)
})
// Listen for changes to the node list
// (e.g. when elements are added or removed)
// and apply updates to the new items
items.on('update', (newItems, oldItems) => {
newItems.forEach((item) => {
item.setAttribute('aria-label', 'New item')
})
})
/** * Consumer * * A simple and lightweight tool to create an * ORM-like consumer for any JSON REST API */
import consume from 'api-consumer'
const api = consume('https://api.example.com')
// GET https://api.example.com/users/123/comments
const userComments = await api.users[123].comments.all()
// POST https://api.example.com/users
const user = await api.users.create({
name: 'Joe Bloggs',
email: 'joe@bloggs.com'
})
// GET https://api.example.com/users/1
const user = await api.users.find(1)
user.name = 'Joseph Bloggs'
// PUT https://api.example.com/users/1
await user.save()
/** * ZUnit * * A powerful testing framework and * assertion library for ZSH projects */
#!/usr/bin/env zunit
@test 'App should run' {
run my-app
assert $state equals 0
assert "$output" same_as "It worked!"
}
@test 'App fails with invalid arg' {
run my-app --invalid-arg
assert $state equals 127
assert "$output" same_as "Invalid argument: --invalid-arg"
}
/** * Async * * A simple PHP library for creating asynchronous processes, * and handling inter-process communication via sockets. */
<?php
use Superrb\Async\Handler;
// Create the handler process
$handler = new Handler(function(int $i): bool {
$this->send('Hi from process '.$i);
return true;
});
// Launch multiple processes concurrently
for ($i = 0; $i < 10; $i++) {
$handler->run($i);
}
// Wait for completion, and check output of each process
$handler->waitAll();
foreach($handler->getMessages() as $message) {
log($message);
}
/** * Phillip * * Meet Phillip, the newest member of your test team. * A PHP testing framework for the 21st century */
<?php
test('Does 1 equal 1', function($t) {
if (1 === 1) {
return $t->pass();
}
return $t->fail('1 does not equal 1');
});
/** * Magic Roundabout * * A tiny JavaScript carousel. * */
const magicRoundabout = new MagicRoundabout(
'#your-element',
{
auto: false,
buttons: true,
center: false,
click: true,
delay: 10000,
draggable: false,
keys: true,
limit: true,
loop: true,
onChange: (slideshow) => {},
touch: true,
scroll: true,
vertical: false
}
)