Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Simple UiKit for complex web apps
JavaScript Ruby
Branch: master
Pull request Compare This branch is 584 commits behind voloko:master.

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.


UKI – simple UiKit for complex Web apps

Uki is a JavaScript user interface toolkit for desktop-like web applications. It comes with a rich view-component library ranging from Sliders to Grids and SplitPanes.

uki({ view: 'Button', text: 'Click me', rect: '10 10 100 24' }).attachTo( window );

uki('Button[text^=Click]').click(function() { alert(this.text()); });


The Web should be simple. Adding a new library to your app should not require using a specific build process or learning a new language. It should be as simple as adding a <script> tag to your HTML. That's exactly the way uki works. No frameworks to install, no dependencies to manage, no CSS to include. Simple.


I can imagine living in a fairy-tale world where any browser supports HTML 5 and there's no such thing as IE6 :) Unfortunately, reality is different. I have to support all the browser flavors available, even the dinosaurs. That's why uki works with IE6+, Opera 9+, FF 2+, Safari 3+, and Chrome. And it looks exactly the same on any of them.


One of the reasons uki appeared is that I had to create a 4000-row complex client-side-searchable table. Just rendering the table made any version of IE unresponsive for half a minute. Uki uses progressive rendering and can render 30k+-row lists and tables mostly instantly.


With all the images packed into it (not supported in IE6 and IE7), a gzipped uki build is under 30kb, and it's a single HTTP request.


Creating a new component requires only one function to redeclare.


It's great when you start building your whole app with uki. But sometimes you have to add a desktop-like experience to a working site. Say, adding a widget or a table to an existing design. While uki can occupy the whole browser window, it works perfectly well in a small <div> in your sidebar. Just use attachTo( myDiv ) for any widget you want to add.

You already know it

Uki is written in plain JavaScript. It leverages well-known DOM and JS idioms such as CSS selectors, events and attributes. If you've ever used jQuery, learning uki won't take long.


Uki doesn't want to be a Jack-of-all-trades. It only does layout, but it does it well. You won't find any AJAX or data-storage-layer code here.

Programmable Layout

HTML and CSS are great technologies, though you can't lay everything out with them. There are things that require programmable layout calculations. Think of a toolbar, which should show a popup when some of the buttons don't fit. Or a split pane. Or moving a slider bar on window resize. That's the reason uki calculates view positions and sizes with JavaScript. Unfortunately, resizing everything with pure JS will make your layout slow as a turtle. So uki uses as much native browser layout as possible.

Longer example

{   // create a split pane...
    view: 'SplitPane', rect: '1000 600', anchors: 'left top right bottom',
    handlePosition: 300, leftMin: 200, rightMin: 300,
    // ...with button on the left
    leftChildViews: { view: 'Button', rect: '10 10 280 24', anchors: 'top left right', text: 'Clear text field' },
    // ...and a vertical split pane on the right...
    rightChildViews: [
       { view: 'VerticalSplitPane', rect: '693 600', anchors: 'left top right bottom', vertical: true,
           // ...with text field in the top part
           topChildViews: { view: 'TextField', rect: '10 10 280 24', anchors: 'top left', value: '0', id: 'field' },
           // ...and a slider in the bottom
           bottomChildViews: { view: 'Slider', rect: '10 10 673 24', anchors: 'top right left' }
}).attachTo( window, '1000 600' );

// on slider change update text field
uki('SplitPane Slider').bind('change', function() {

// on button click clear the text field
uki('Button[text~="Clear"]').bind('click', function() {
    uki('#field').value('') // find by id

results in


    view: 'Button',
    rect: '400px 40px 200px 24px',
    text: 'uki is awesome!'
}).attachTo( document.getElementById('test'), '1000px 100px' );

uki('Button[text^=uki]').click(function() {
    alert('Hello world!');

  { view: "Button", text: "Click me", rect: "10 10 100 24" }
).attachTo( document.getElementById("test") );

  function() { alert(this.text()); }

uki({ view: 'SplitPane', rect: '1000 600', autosize: 'width height', anchors: 'left top right bottom', 
    handlePosition: 300, autogrowLeft: false, leftMin: 300, rightMin: 300,
    leftChildViews: { view: 'Button', rect: '10 10 280 24', anchors: 'top left right', text: 'left pane', autosize: 'width' },
    rightChildViews: [
        { view: 'SplitPane', rect: '693 600', autosize: 'width height', anchors: 'left top right bottom', vertical: true,
            topChildViews: { view: 'Button', rect: '10 10 280 24', anchors: 'top left', text: 'top pane' },
            bottomChildViews: { view: 'Slider', rect: '10 10 673 24', anchors: 'top right left', autosize: 'width' }
}).attachTo( window, '1000 600' );


cd tests
narwhal runner.js


And one more thing. Unobtrusive JavaScript is evil.

Something went wrong with that request. Please try again.