While there may be no one thing that looks great on all consoles, I will point out that the simpler your layout is the more consistent it will appear when the user changes console size.
I have a fully implemented solution if you want C code that works with ncurses, but it is complicated.
It uses 'layouts' which partition the screen into regions kind of like HTML tables with spanning cells. Each region is associated with a drawing routine and minimum/maximum x and y sizes. There's a layout manager that keeps track of as many layouts as you want, so you can decide which layout to use based on the xsize and ysize of the display, or whatever other criteria. Whether character information goes in a column on the left edge instead of in a row at the bottom can depend on aspect ratio, or whether you even allot regions for a map key or a mini-map can depend on the size of the display, or you may give the player a UI option to choose between 'nethackish' or 'angbandian' or 'alphamanly' layouts.
The minimums are treated as absolute; if the screen is too small to meet all the region minimums, resizing fails. From there, resizing adds rows or columns of cells one at a time. It decides what table row/column to add a character row/column to, by sorting the regions in order of "tightness" (ie, what percentage of their desired maximums they have been allotted already) and going down the list iteratively eliminating table rows/columns from consideration unless they are spanned by the "tightest" region not already considered. Eventually, usually after considering just one or two or three regions, it narrows it down to a single table row/column to add the new row/column of character cells to. Rinse, repeat, until you reach the xsize and ysize of the display.
The UI draws the layout when the game is getting input from the user, by calling the draw routine for the active layout, which in turn calls the draw routine for each region. The drawing routines get as arguments the coordinates they're responsible for filling in, plus a small bundle of arguments from the UI.
The drawing routines I've got depend heavily on an interface provided by a display manager. Displays are a different idea from layouts. Displays and draw routines would be totally different with a graphical display at pixel resolution, while the layout code wouldn't change at all. The integers it uses for the size of its table rows and columns would just refer to pixel coordinates instead of character cells. Anyway, the display manager I've implemented is for character displays and depend on ncurses.
The interface provided by the Display manager could be exactly the same for a tiled display as it is for the ncurses display, and should work without changing the draw routines. I wrote the display manager with the plan that it should be the *only* file containing code that needs to change if switching from ncurses to tiles.
In fact the display manager and layout manager are pretty object-oriented; function pointers are just the C representation of an object method.
So the UI knows about the layout interface. The layout code essentially knows nothing about the display it's managing except that it has function pointers to draw routines. It knows it can give them coordinates as arguments and it passes through arguments to them from the UI. The draw routines know about the interface provided by the display in terms of areas to "say" text in and grids of rows and columns to "draw" tiles in. In fact the tiles are characters with ncurses attributes just like the strings, but the display routines don't know that; they just have a tile number that represents a tile and they tell the display to put that tile at a given location in the region.
So anyway... if you want to deal with C code that uses function pointers etc, I have code written and documented and although some of it is kinda messy, I will cheerfully share it.