Hi,

As you described, using a clist to store the set of available fonts
and using \str_case to provide the various codes is a bit error-prone,
and the correct structure is a property list matching directly each
font name to the corresponding code.

> \prop_new:N \g_jcs_fontcode_prop
>
> \prop_gput:Nnn \g_jcs_fontcode_prop { lmodern } {  }
>
> \bool_if:nT { \xetex_if_engine_p: || \luatex_if_engine_p: }
>   { \prop_gput:Nnn \g_jcs_fontcode_prop { fontin } {  } }

If you have many fonts, it makes sense to introduce a function that
puts code into the prop (that's a very simple wrapper, but helps to
see what you are doing).

    \prop_new:N \g_jcs_fontcode_prop
    \cs_new_protected:Npn \jcs_define_font:nn #1
      { \prop_gput:Nnn \g_ics_fontcode_prop {#1} }
    \jcs_define_font:nn { lmodern } {  }
    \bool_if:nT { \xetex_if_engine_p: || \luatex_if_engine_p: }
      { \jcs_define_font:nn { fontin } {  } }
    ...

The part of your code where you extract the code for the font and
store it in order to apply it after all options have been processed is
indeed the correct way to go.  However, since you are giving an
".initial:V" value which always sets \g_jcs_fontcode_tl at key
definition time, you can simplify the F branch of \prop_get:NnNTF as
follows

         \prop_get:NnNTF \g_jcs_fontcode_prop {#1} \l_jcs_fontcode_tl
           { \tl_gset_eq:NN \g_jcs_fontcode_tl \l_jcs_fontcode_tl }
           { \msg_warning:nnn { jcsres } { unknown-font-option } { #1 } }

> Does this begin to sound like the way forward?

Yes

Bruno